]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/HttpChannel.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / server / HttpChannel.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.net.InetSocketAddress;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.ClosedChannelException;
25 import java.nio.charset.StandardCharsets;
26 import java.util.concurrent.atomic.AtomicBoolean;
27 import java.util.concurrent.atomic.AtomicInteger;
28
29 import javax.servlet.DispatcherType;
30 import javax.servlet.RequestDispatcher;
31 import javax.servlet.http.HttpServletRequest;
32
33 import org.eclipse.jetty.http.HttpField;
34 import org.eclipse.jetty.http.HttpFields;
35 import org.eclipse.jetty.http.HttpGenerator;
36 import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
37 import org.eclipse.jetty.http.HttpHeader;
38 import org.eclipse.jetty.http.HttpHeaderValue;
39 import org.eclipse.jetty.http.HttpMethod;
40 import org.eclipse.jetty.http.HttpParser;
41 import org.eclipse.jetty.http.HttpStatus;
42 import org.eclipse.jetty.http.HttpURI;
43 import org.eclipse.jetty.http.HttpVersion;
44 import org.eclipse.jetty.http.MimeTypes;
45 import org.eclipse.jetty.io.ByteBufferPool;
46 import org.eclipse.jetty.io.ChannelEndPoint;
47 import org.eclipse.jetty.io.EndPoint;
48 import org.eclipse.jetty.io.EofException;
49 import org.eclipse.jetty.server.HttpChannelState.Action;
50 import org.eclipse.jetty.server.handler.ContextHandler;
51 import org.eclipse.jetty.server.handler.ErrorHandler;
52 import org.eclipse.jetty.util.Callback;
53 import org.eclipse.jetty.util.SharedBlockingCallback.Blocker;
54 import org.eclipse.jetty.util.URIUtil;
55 import org.eclipse.jetty.util.log.Log;
56 import org.eclipse.jetty.util.log.Logger;
57 import org.eclipse.jetty.util.thread.Scheduler;
58
59
60 /* ------------------------------------------------------------ */
61 /** HttpChannel.
62  * Represents a single endpoint for HTTP semantic processing.
63  * The HttpChannel is both a HttpParser.RequestHandler, where it passively receives events from
64  * an incoming HTTP request, and a Runnable, where it actively takes control of the request/response
65  * life cycle and calls the application (perhaps suspending and resuming with multiple calls to run).
66  * The HttpChannel signals the switch from passive mode to active mode by returning true to one of the
67  * HttpParser.RequestHandler callbacks.   The completion of the active phase is signalled by a call to
68  * HttpTransport.completed().
69  *
70  */
71 public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, HttpParser.ProxyHandler
72 {
73     private static final Logger LOG = Log.getLogger(HttpChannel.class);
74     private static final ThreadLocal<HttpChannel<?>> __currentChannel = new ThreadLocal<>();
75
76     /* ------------------------------------------------------------ */
77     /** Get the current channel that this thread is dispatched to.
78      * @see Request#getAttribute(String) for a more general way to access the HttpChannel
79      * @return the current HttpChannel or null
80      */
81     public static HttpChannel<?> getCurrentHttpChannel()
82     {
83         return __currentChannel.get();
84     }
85
86     protected static HttpChannel<?> setCurrentHttpChannel(HttpChannel<?> channel)
87     {
88         HttpChannel<?> last=__currentChannel.get();
89         if (channel==null)
90             __currentChannel.remove();
91         else 
92             __currentChannel.set(channel);
93         return last;
94     }
95
96     private final AtomicBoolean _committed = new AtomicBoolean();
97     private final AtomicInteger _requests = new AtomicInteger();
98     private final Connector _connector;
99     private final HttpConfiguration _configuration;
100     private final EndPoint _endPoint;
101     private final HttpTransport _transport;
102     private final HttpURI _uri;
103     private final HttpChannelState _state;
104     private final Request _request;
105     private final Response _response;
106     private HttpVersion _version = HttpVersion.HTTP_1_1;
107     private boolean _expect = false;
108     private boolean _expect100Continue = false;
109     private boolean _expect102Processing = false;
110
111     public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<T> input)
112     {
113         _connector = connector;
114         _configuration = configuration;
115         _endPoint = endPoint;
116         _transport = transport;
117
118         _uri = new HttpURI(URIUtil.__CHARSET);
119         _state = new HttpChannelState(this);
120         input.init(_state);
121         _request = new Request(this, input);
122         _response = new Response(this, new HttpOutput(this));
123     }
124
125     public HttpChannelState getState()
126     {
127         return _state;
128     }
129
130     public HttpVersion getHttpVersion()
131     {
132         return _version;
133     }
134     /**
135      * @return the number of requests handled by this connection
136      */
137     public int getRequests()
138     {
139         return _requests.get();
140     }
141
142     public Connector getConnector()
143     {
144         return _connector;
145     }
146
147     public HttpTransport getHttpTransport()
148     {
149         return _transport;
150     }
151     
152     public ByteBufferPool getByteBufferPool()
153     {
154         return _connector.getByteBufferPool();
155     }
156
157     public HttpConfiguration getHttpConfiguration()
158     {
159         return _configuration;
160     }
161
162     public Server getServer()
163     {
164         return _connector.getServer();
165     }
166
167     public Request getRequest()
168     {
169         return _request;
170     }
171
172     public Response getResponse()
173     {
174         return _response;
175     }
176
177     public EndPoint getEndPoint()
178     {
179         return _endPoint;
180     }
181
182     public InetSocketAddress getLocalAddress()
183     {
184         return _endPoint.getLocalAddress();
185     }
186
187     public InetSocketAddress getRemoteAddress()
188     {
189         return _endPoint.getRemoteAddress();
190     }
191
192     @Override
193     public int getHeaderCacheSize()
194     {
195         return _configuration.getHeaderCacheSize();
196     }
197
198     /**
199      * If the associated response has the Expect header set to 100 Continue,
200      * then accessing the input stream indicates that the handler/servlet
201      * is ready for the request body and thus a 100 Continue response is sent.
202      *
203      * @throws IOException if the InputStream cannot be created
204      */
205     public void continue100(int available) throws IOException
206     {
207         // If the client is expecting 100 CONTINUE, then send it now.
208         // TODO: consider using an AtomicBoolean ?
209         if (isExpecting100Continue())
210         {
211             _expect100Continue = false;
212
213             // is content missing?
214             if (available == 0)
215             {
216                 if (_response.isCommitted())
217                     throw new IOException("Committed before 100 Continues");
218
219                 // TODO: break this dependency with HttpGenerator
220                 boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
221                 if (!committed)
222                     throw new IOException("Concurrent commit while trying to send 100-Continue");
223             }
224         }
225     }
226
227     public void reset()
228     {
229         _committed.set(false);
230         _expect = false;
231         _expect100Continue = false;
232         _expect102Processing = false;
233         _request.recycle();
234         _response.recycle();
235         _uri.clear();
236     }
237
238     @Override
239     public void run()
240     {
241         handle();
242     }
243
244     /* ------------------------------------------------------------ */
245     /**
246      * @return True if the channel is ready to continue handling (ie it is not suspended)
247      */
248     public boolean handle()
249     {
250         LOG.debug("{} handle enter", this);
251
252         final HttpChannel<?>last = setCurrentHttpChannel(this);
253
254         String threadName = null;
255         if (LOG.isDebugEnabled())
256         {
257             threadName = Thread.currentThread().getName();
258             Thread.currentThread().setName(threadName + " - " + _uri);
259         }
260
261         HttpChannelState.Action action = _state.handling();
262         try
263         {
264             // Loop here to handle async request redispatches.
265             // The loop is controlled by the call to async.unhandle in the
266             // finally block below.  Unhandle will return false only if an async dispatch has
267             // already happened when unhandle is called.
268             loop: while (action.ordinal()<HttpChannelState.Action.WAIT.ordinal() && getServer().isRunning())
269             {
270                 boolean error=false;
271                 try
272                 {
273                     LOG.debug("{} action {}",this,action);
274
275                     switch(action)
276                     {
277                         case REQUEST_DISPATCH:
278                             _request.setHandled(false);
279                             _response.getHttpOutput().reopen();
280                             _request.setDispatcherType(DispatcherType.REQUEST);
281
282                             for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers())
283                                 customizer.customize(getConnector(),_configuration,_request);
284                             getServer().handle(this);
285                             break;
286
287                         case ASYNC_DISPATCH:
288                             _request.setHandled(false);
289                             _response.getHttpOutput().reopen();
290                             _request.setDispatcherType(DispatcherType.ASYNC);
291                             getServer().handleAsync(this);
292                             break;
293
294                         case ASYNC_EXPIRED:
295                             _request.setHandled(false);
296                             _response.getHttpOutput().reopen();
297                             _request.setDispatcherType(DispatcherType.ERROR);
298
299                             Throwable ex=_state.getAsyncContextEvent().getThrowable();
300                             String reason="Async Timeout";
301                             if (ex!=null)
302                             {
303                                 reason="Async Exception";
304                                 _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
305                             }
306                             _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
307                             _request.setAttribute(RequestDispatcher.ERROR_MESSAGE,reason);
308                             _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,_request.getRequestURI());
309
310                             _response.setStatusWithReason(500,reason);
311
312                             
313                             ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(),_state.getContextHandler());                                
314                             if (eh instanceof ErrorHandler.ErrorPageMapper)
315                             {
316                                 String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
317                                 if (error_page!=null)
318                                     _state.getAsyncContextEvent().setDispatchPath(error_page);
319                             }
320
321                             getServer().handleAsync(this);
322                             break;
323
324                         case READ_CALLBACK:
325                         {
326                             ContextHandler handler=_state.getContextHandler();
327                             if (handler!=null)
328                                 handler.handle(_request.getHttpInput());
329                             else
330                                 _request.getHttpInput().run();
331                             break;
332                         }
333
334                         case WRITE_CALLBACK:
335                         {
336                             ContextHandler handler=_state.getContextHandler();
337
338                             if (handler!=null)
339                                 handler.handle(_response.getHttpOutput());
340                             else
341                                 _response.getHttpOutput().run();
342                             break;
343                         }   
344
345                         default:
346                             break loop;
347
348                     }
349                 }
350                 catch (Error e)
351                 {
352                     if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
353                         LOG.ignore(e);
354                     else
355                     {
356                         error=true;
357                         throw e;
358                     }
359                 }
360                 catch (Exception e)
361                 {
362                     error=true;
363                     if (e instanceof EofException)
364                         LOG.debug(e);
365                     else
366                         LOG.warn(String.valueOf(_uri), e);
367                     _state.error(e);
368                     _request.setHandled(true);
369                     handleException(e);
370                 }
371                 finally
372                 {
373                     if (error && _state.isAsyncStarted())
374                         _state.errorComplete();
375                     action = _state.unhandle();
376                 }
377             }
378
379         }
380         finally
381         {
382             setCurrentHttpChannel(last);
383             if (threadName != null && LOG.isDebugEnabled())
384                 Thread.currentThread().setName(threadName);
385         }
386
387         if (action==Action.COMPLETE)
388         {
389             try
390             {
391                 _state.completed();
392
393                 if (!_response.isCommitted() && !_request.isHandled())
394                     _response.sendError(404);
395                 else
396                     // Complete generating the response
397                     _response.closeOutput();
398             }
399             catch(EofException|ClosedChannelException e)
400             {
401                 LOG.debug(e);
402             }
403             catch(Exception e)
404             {
405                 LOG.warn("complete failed",e);
406             }
407             finally
408             {
409                 _request.setHandled(true);
410                 _transport.completed();
411             }
412         }
413
414         LOG.debug("{} handle exit, result {}", this, action);
415
416         return action!=Action.WAIT;
417     }
418
419     /**
420      * <p>Sends an error 500, performing a special logic to detect whether the request is suspended,
421      * to avoid concurrent writes from the application.</p>
422      * <p>It may happen that the application suspends, and then throws an exception, while an application
423      * spawned thread writes the response content; in such case, we attempt to commit the error directly
424      * bypassing the {@link ErrorHandler} mechanisms and the response OutputStream.</p>
425      *
426      * @param x the Throwable that caused the problem
427      */
428     protected void handleException(Throwable x)
429     {
430         try
431         {
432             _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,x);
433             _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,x.getClass());
434             if (_state.isSuspended())
435             {
436                 HttpFields fields = new HttpFields();
437                 fields.add(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
438                 ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, HttpStatus.INTERNAL_SERVER_ERROR_500, null, _request.isHead());
439                 boolean committed = sendResponse(info, null, true);
440                 if (!committed)
441                     LOG.warn("Could not send response error 500: "+x);
442                 _request.getAsyncContext().complete();
443             }
444             else if (isCommitted())
445             {
446                 _transport.abort();
447                 if (!(x instanceof EofException))
448                     LOG.warn("Could not send response error 500: "+x);
449             }
450             else
451             {
452                 _response.setHeader(HttpHeader.CONNECTION.asString(),HttpHeaderValue.CLOSE.asString());
453                 _response.sendError(500, x.getMessage());
454             }
455         }
456         catch (IOException e)
457         {
458             // We tried our best, just log
459             LOG.debug("Could not commit response error 500", e);
460         }
461     }
462
463     public boolean isExpecting100Continue()
464     {
465         return _expect100Continue;
466     }
467
468     public boolean isExpecting102Processing()
469     {
470         return _expect102Processing;
471     }
472
473     @Override
474     public String toString()
475     {
476         return String.format("%s@%x{r=%s,a=%s,uri=%s}",
477                 getClass().getSimpleName(),
478                 hashCode(),
479                 _requests,
480                 _state.getState(),
481                 _state.getState()==HttpChannelState.State.IDLE?"-":_request.getRequestURI()
482             );
483     }
484
485     @Override
486     public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
487     {
488         _request.setAttribute("PROXY", protocol);
489         _request.setServerName(sAddr);
490         _request.setServerPort(dPort);
491         _request.setRemoteAddr(InetSocketAddress.createUnresolved(sAddr,sPort));
492     }
493     
494     @Override
495     public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
496     {
497         _expect = false;
498         _expect100Continue = false;
499         _expect102Processing = false;
500
501         _request.setTimeStamp(System.currentTimeMillis());
502         _request.setMethod(httpMethod, method);
503
504         if (httpMethod == HttpMethod.CONNECT)
505             _uri.parseConnect(uri.array(),uri.arrayOffset()+uri.position(),uri.remaining());
506         else
507             _uri.parse(uri.array(),uri.arrayOffset()+uri.position(),uri.remaining());
508         _request.setUri(_uri);
509
510         String path;
511         try
512         {
513             path = _uri.getDecodedPath();
514         }
515         catch (Exception e)
516         {
517             LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
518             LOG.ignore(e);
519             path = _uri.getDecodedPath(StandardCharsets.ISO_8859_1);
520         }
521         
522         String info = URIUtil.canonicalPath(path);
523
524         if (info == null)
525         {
526             if( path==null && _uri.getScheme()!=null &&_uri.getHost()!=null)
527             {
528                 info = "/";
529                 _request.setRequestURI("");
530             }
531             else
532             {
533                 badMessage(400,null);
534                 return true;
535             }
536         }
537         _request.setPathInfo(info);
538         _version = version == null ? HttpVersion.HTTP_0_9 : version;
539         _request.setHttpVersion(_version);
540
541         return false;
542     }
543
544     @Override
545     public boolean parsedHeader(HttpField field)
546     {
547         HttpHeader header=field.getHeader();
548         String value=field.getValue();
549         if (value == null)
550             value = "";
551         if (header != null)
552         {
553             switch (header)
554             {
555                 case EXPECT:
556                     if (_version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
557                     {
558                         HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
559                         switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
560                         {
561                             case CONTINUE:
562                                 _expect100Continue = true;
563                                 break;
564
565                             case PROCESSING:
566                                 _expect102Processing = true;
567                                 break;
568
569                             default:
570                                 String[] values = value.split(",");
571                                 for (int i = 0; values != null && i < values.length; i++)
572                                 {
573                                     expect = HttpHeaderValue.CACHE.get(values[i].trim());
574                                     if (expect == null)
575                                         _expect = true;
576                                     else
577                                     {
578                                         switch (expect)
579                                         {
580                                             case CONTINUE:
581                                                 _expect100Continue = true;
582                                                 break;
583                                             case PROCESSING:
584                                                 _expect102Processing = true;
585                                                 break;
586                                             default:
587                                                 _expect = true;
588                                         }
589                                     }
590                                 }
591                         }
592                     }
593                     break;
594
595                 case CONTENT_TYPE:
596                     MimeTypes.Type mime = MimeTypes.CACHE.get(value);
597                     String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(value) : mime.getCharset().toString();
598                     if (charset != null)
599                         _request.setCharacterEncodingUnchecked(charset);
600                     break;
601                 default:
602             }
603         }
604
605         if (field.getName()!=null)
606             _request.getHttpFields().add(field);
607         return false;
608     }
609
610     @Override
611     public boolean parsedHostHeader(String host, int port)
612     {
613         if (_uri.getHost()==null)
614         {
615             _request.setServerName(host);
616             _request.setServerPort(port);
617         }
618         return false;
619     }
620
621     @Override
622     public boolean headerComplete()
623     {
624         _requests.incrementAndGet();
625         switch (_version)
626         {
627             case HTTP_0_9:
628                 break;
629
630             case HTTP_1_0:
631                 if (_configuration.getSendDateHeader())
632                     _response.getHttpFields().put(_connector.getServer().getDateField());
633                 break;
634
635             case HTTP_1_1:
636                 if (_configuration.getSendDateHeader())
637                     _response.getHttpFields().put(_connector.getServer().getDateField());
638
639                 if (_expect)
640                 {
641                     badMessage(HttpStatus.EXPECTATION_FAILED_417,null);
642                     return true;
643                 }
644
645                 break;
646
647             default:
648                 throw new IllegalStateException();
649         }
650
651         return true;
652     }
653
654     @Override
655     public boolean content(T item)
656     {
657         if (LOG.isDebugEnabled())
658             LOG.debug("{} content {}", this, item);
659         @SuppressWarnings("unchecked")
660         HttpInput<T> input = (HttpInput<T>)_request.getHttpInput();
661         input.content(item);
662
663         return false;
664     }
665
666     @Override
667     public boolean messageComplete()
668     {
669         LOG.debug("{} messageComplete", this);
670         _request.getHttpInput().messageComplete();
671         return true;
672     }
673
674     @Override
675     public void earlyEOF()
676     {
677         _request.getHttpInput().earlyEOF();
678     }
679
680     @Override
681     public void badMessage(int status, String reason)
682     {
683         if (status < 400 || status > 599)
684             status = HttpStatus.BAD_REQUEST_400;
685
686         try
687         {
688             if (_state.handling()==Action.REQUEST_DISPATCH)
689             {
690                 ByteBuffer content=null;
691                 HttpFields fields=new HttpFields();
692
693                 ErrorHandler handler=getServer().getBean(ErrorHandler.class);
694                 if (handler!=null)
695                     content=handler.badMessageError(status,reason,fields);
696
697                 sendResponse(new ResponseInfo(HttpVersion.HTTP_1_1,fields,0,status,reason,false),content ,true);
698             }
699         }
700         catch (IOException e)
701         {
702             LOG.debug(e);
703         }
704         finally
705         {
706             if (_state.unhandle()==Action.COMPLETE)
707                 _state.completed();
708             else 
709                 throw new IllegalStateException();
710         }
711     }
712
713     protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete, final Callback callback)
714     {
715         // TODO check that complete only set true once by changing _committed to AtomicRef<Enum>
716         boolean committing = _committed.compareAndSet(false, true);
717         if (committing)
718         {
719             // We need an info to commit
720             if (info==null)
721                 info = _response.newResponseInfo();
722
723             // wrap callback to process 100 or 500 responses
724             final int status=info.getStatus();
725             final Callback committed = (status<200&&status>=100)?new Commit100Callback(callback):new CommitCallback(callback);
726
727             // committing write
728             _transport.send(info, content, complete, committed);
729         }
730         else if (info==null)
731         {
732             // This is a normal write
733             _transport.send(content, complete, callback);
734         }
735         else
736         {
737             callback.failed(new IllegalStateException("committed"));
738         }
739         return committing;
740     }
741
742     protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
743     {
744         try(Blocker blocker = _response.getHttpOutput().acquireWriteBlockingCallback())
745         {
746             boolean committing = sendResponse(info,content,complete,blocker);
747             blocker.block();
748             return committing;
749         }
750     }
751
752     public boolean isCommitted()
753     {
754         return _committed.get();
755     }
756
757     /**
758      * <p>Non-Blocking write, committing the response if needed.</p>
759      *
760      * @param content  the content buffer to write
761      * @param complete whether the content is complete for the response
762      * @param callback Callback when complete or failed
763      */
764     protected void write(ByteBuffer content, boolean complete, Callback callback)
765     {
766         sendResponse(null,content,complete,callback);
767     }
768
769     protected void execute(Runnable task)
770     {
771         _connector.getExecutor().execute(task);
772     }
773
774     public Scheduler getScheduler()
775     {
776         return _connector.getScheduler();
777     }
778
779     /* ------------------------------------------------------------ */
780     /**
781      * @return true if the HttpChannel can efficiently use direct buffer (typically this means it is not over SSL or a multiplexed protocol)
782      */
783     public boolean useDirectBuffers()
784     {
785         return getEndPoint() instanceof ChannelEndPoint;
786     }
787
788     /**
789      * If a write or similar to this channel fails this method should be called. The standard implementation
790      * of {@link #failed()} is a noop. But the different implementations of HttpChannel might want to take actions.
791      */
792     public void failed()
793     {
794     }
795
796     private class CommitCallback implements Callback
797     {
798         private final Callback _callback;
799
800         private CommitCallback(Callback callback)
801         {
802             _callback = callback;
803         }
804
805         @Override
806         public void succeeded()
807         {
808             _callback.succeeded();
809         }
810
811         @Override
812         public void failed(final Throwable x)
813         {
814             if (x instanceof EofException || x instanceof ClosedChannelException)
815             {
816                 LOG.debug(x);
817                 _callback.failed(x);
818                 _response.getHttpOutput().closed();
819             }
820             else
821             {
822                 LOG.warn("Commit failed",x);
823                 _transport.send(HttpGenerator.RESPONSE_500_INFO,null,true,new Callback()
824                 {
825                     @Override
826                     public void succeeded()
827                     {
828                         _callback.failed(x);
829                         _response.getHttpOutput().closed();
830                     }
831
832                     @Override
833                     public void failed(Throwable th)
834                     {
835                         LOG.ignore(th);
836                         _callback.failed(x);
837                         _response.getHttpOutput().closed();
838                     }
839                 });
840             }
841         }
842     }
843
844     private class Commit100Callback extends CommitCallback
845     {
846         private Commit100Callback(Callback callback)
847         {
848             super(callback);
849         }
850
851         @Override
852         public void succeeded()
853         {
854              _committed.set(false);
855              super.succeeded();
856         }
857
858     }
859
860 }