View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.nio.protocol;
29  
30  import java.io.IOException;
31  import java.io.OutputStream;
32  import java.util.concurrent.Executor;
33  
34  import org.apache.http.ConnectionReuseStrategy;
35  import org.apache.http.HttpEntity;
36  import org.apache.http.HttpEntityEnclosingRequest;
37  import org.apache.http.HttpException;
38  import org.apache.http.HttpRequest;
39  import org.apache.http.HttpResponse;
40  import org.apache.http.HttpResponseFactory;
41  import org.apache.http.HttpStatus;
42  import org.apache.http.HttpVersion;
43  import org.apache.http.MethodNotSupportedException;
44  import org.apache.http.ProtocolException;
45  import org.apache.http.ProtocolVersion;
46  import org.apache.http.UnsupportedHttpVersionException;
47  import org.apache.http.annotation.ThreadSafe;
48  import org.apache.http.entity.ByteArrayEntity;
49  import org.apache.http.nio.ContentDecoder;
50  import org.apache.http.nio.ContentEncoder;
51  import org.apache.http.nio.IOControl;
52  import org.apache.http.nio.NHttpConnection;
53  import org.apache.http.nio.NHttpServerConnection;
54  import org.apache.http.nio.NHttpServiceHandler;
55  import org.apache.http.nio.entity.ContentBufferEntity;
56  import org.apache.http.nio.entity.ContentOutputStream;
57  import org.apache.http.nio.params.NIOReactorPNames;
58  import org.apache.http.nio.util.ByteBufferAllocator;
59  import org.apache.http.nio.util.ContentInputBuffer;
60  import org.apache.http.nio.util.ContentOutputBuffer;
61  import org.apache.http.nio.util.DirectByteBufferAllocator;
62  import org.apache.http.nio.util.SharedInputBuffer;
63  import org.apache.http.nio.util.SharedOutputBuffer;
64  import org.apache.http.params.DefaultedHttpParams;
65  import org.apache.http.params.HttpParams;
66  import org.apache.http.protocol.ExecutionContext;
67  import org.apache.http.protocol.HttpContext;
68  import org.apache.http.protocol.HttpExpectationVerifier;
69  import org.apache.http.protocol.HttpProcessor;
70  import org.apache.http.protocol.HttpRequestHandler;
71  import org.apache.http.protocol.HttpRequestHandlerResolver;
72  import org.apache.http.util.Args;
73  import org.apache.http.util.EncodingUtils;
74  import org.apache.http.util.EntityUtils;
75  
76  /**
77   * Service protocol handler implementation that provide compatibility with
78   * the blocking I/O by utilizing shared content buffers and a fairly small pool
79   * of worker threads. The throttling protocol handler allocates input / output
80   * buffers of a constant length upon initialization and controls the rate of
81   * I/O events in order to ensure those content buffers do not ever get
82   * overflown. This helps ensure nearly constant memory footprint for HTTP
83   * connections and avoid the out of memory condition while streaming content
84   * in and out. The {@link HttpRequestHandler#handle(HttpRequest, HttpResponse, HttpContext)}
85   * method will fire immediately when a message is received. The protocol handler
86   * delegate the task of processing requests and generating response content to
87   * an {@link Executor}, which is expected to perform those tasks using
88   * dedicated worker threads in order to avoid blocking the I/O thread.
89   * <p/>
90   * Usually throttling protocol handlers need only a modest number of worker
91   * threads, much fewer than the number of concurrent connections. If the length
92   * of the message is smaller or about the size of the shared content buffer
93   * worker thread will just store content in the buffer and terminate almost
94   * immediately without blocking. The I/O dispatch thread in its turn will take
95   * care of sending out the buffered content asynchronously. The worker thread
96   * will have to block only when processing large messages and the shared buffer
97   * fills up. It is generally advisable to allocate shared buffers of a size of
98   * an average content body for optimal performance.
99   * <p>
100  * The following parameters can be used to customize the behavior of this
101  * class:
102  * <ul>
103  *  <li>{@link org.apache.http.nio.params.NIOReactorPNames#CONTENT_BUFFER_SIZE}</li>
104  *  <li>{@link org.apache.http.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}</li>
105  * </ul>
106  *
107  * @since 4.0
108  *
109  * @deprecated (4.2) use {@link HttpAsyncService}
110  */
111 @Deprecated
112 @ThreadSafe // provided injected dependencies are immutable or thread safe
113 public class ThrottlingHttpServiceHandler extends NHttpHandlerBase
114                                           implements NHttpServiceHandler {
115 
116     protected final HttpResponseFactory responseFactory;
117     protected final Executor executor;
118 
119     protected HttpRequestHandlerResolver handlerResolver;
120     protected HttpExpectationVerifier expectationVerifier;
121 
122     private final int bufsize;
123 
124     public ThrottlingHttpServiceHandler(
125             final HttpProcessor httpProcessor,
126             final HttpResponseFactory responseFactory,
127             final ConnectionReuseStrategy connStrategy,
128             final ByteBufferAllocator allocator,
129             final Executor executor,
130             final HttpParams params) {
131         super(httpProcessor, connStrategy, allocator, params);
132         Args.notNull(responseFactory, "Response factory");
133         Args.notNull(executor, "Executor");
134         this.responseFactory = responseFactory;
135         this.executor = executor;
136         this.bufsize = this.params.getIntParameter(NIOReactorPNames.CONTENT_BUFFER_SIZE, 20480);
137     }
138 
139     public ThrottlingHttpServiceHandler(
140             final HttpProcessor httpProcessor,
141             final HttpResponseFactory responseFactory,
142             final ConnectionReuseStrategy connStrategy,
143             final Executor executor,
144             final HttpParams params) {
145         this(httpProcessor, responseFactory, connStrategy,
146                 DirectByteBufferAllocator.INSTANCE, executor, params);
147     }
148 
149     public void setHandlerResolver(final HttpRequestHandlerResolver handlerResolver) {
150         this.handlerResolver = handlerResolver;
151     }
152 
153     public void setExpectationVerifier(final HttpExpectationVerifier expectationVerifier) {
154         this.expectationVerifier = expectationVerifier;
155     }
156 
157     public void connected(final NHttpServerConnection conn) {
158         final HttpContext context = conn.getContext();
159 
160         final ServerConnState connState = new ServerConnState(this.bufsize, conn, allocator);
161         context.setAttribute(CONN_STATE, connState);
162 
163         if (this.eventListener != null) {
164             this.eventListener.connectionOpen(conn);
165         }
166     }
167 
168     public void closed(final NHttpServerConnection conn) {
169         final HttpContext context = conn.getContext();
170         final ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
171 
172         if (connState != null) {
173             synchronized (connState) {
174                 connState.close();
175                 connState.notifyAll();
176             }
177         }
178 
179         if (this.eventListener != null) {
180             this.eventListener.connectionClosed(conn);
181         }
182     }
183 
184     public void exception(final NHttpServerConnection conn, final HttpException httpex) {
185         if (conn.isResponseSubmitted()) {
186             if (eventListener != null) {
187                 eventListener.fatalProtocolException(httpex, conn);
188             }
189             return;
190         }
191 
192         final HttpContext context = conn.getContext();
193 
194         final ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
195 
196         try {
197 
198             final HttpResponse response = this.responseFactory.newHttpResponse(
199                     HttpVersion.HTTP_1_0,
200                     HttpStatus.SC_INTERNAL_SERVER_ERROR,
201                     context);
202             response.setParams(
203                     new DefaultedHttpParams(response.getParams(), this.params));
204             handleException(httpex, response);
205             response.setEntity(null);
206 
207             this.httpProcessor.process(response, context);
208 
209             synchronized (connState) {
210                 connState.setResponse(response);
211                 // Response is ready to be committed
212                 conn.requestOutput();
213             }
214 
215         } catch (final IOException ex) {
216             shutdownConnection(conn, ex);
217             if (eventListener != null) {
218                 eventListener.fatalIOException(ex, conn);
219             }
220         } catch (final HttpException ex) {
221             closeConnection(conn, ex);
222             if (eventListener != null) {
223                 eventListener.fatalProtocolException(ex, conn);
224             }
225         }
226     }
227 
228     public void exception(final NHttpServerConnection conn, final IOException ex) {
229         shutdownConnection(conn, ex);
230 
231         if (this.eventListener != null) {
232             this.eventListener.fatalIOException(ex, conn);
233         }
234     }
235 
236     public void timeout(final NHttpServerConnection conn) {
237         handleTimeout(conn);
238     }
239 
240     public void requestReceived(final NHttpServerConnection conn) {
241         final HttpContext context = conn.getContext();
242 
243         final HttpRequest request = conn.getHttpRequest();
244         final ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
245 
246         synchronized (connState) {
247             boolean contentExpected = false;
248             if (request instanceof HttpEntityEnclosingRequest) {
249                 final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
250                 if (entity != null) {
251                     contentExpected = true;
252                 }
253             }
254 
255             if (!contentExpected) {
256                 conn.suspendInput();
257             }
258 
259             this.executor.execute(new Runnable() {
260 
261                 public void run() {
262                     try {
263 
264                         handleRequest(request, connState, conn);
265 
266                     } catch (final IOException ex) {
267                         shutdownConnection(conn, ex);
268                         if (eventListener != null) {
269                             eventListener.fatalIOException(ex, conn);
270                         }
271                     } catch (final HttpException ex) {
272                         shutdownConnection(conn, ex);
273                         if (eventListener != null) {
274                             eventListener.fatalProtocolException(ex, conn);
275                         }
276                     }
277                 }
278 
279             });
280 
281             connState.notifyAll();
282         }
283 
284     }
285 
286     public void inputReady(final NHttpServerConnection conn, final ContentDecoder decoder) {
287         final HttpContext context = conn.getContext();
288 
289         final ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
290 
291         try {
292 
293             synchronized (connState) {
294                 final ContentInputBuffer buffer = connState.getInbuffer();
295 
296                 buffer.consumeContent(decoder);
297                 if (decoder.isCompleted()) {
298                     connState.setInputState(ServerConnState.REQUEST_BODY_DONE);
299                 } else {
300                     connState.setInputState(ServerConnState.REQUEST_BODY_STREAM);
301                 }
302 
303                 connState.notifyAll();
304             }
305 
306         } catch (final IOException ex) {
307             shutdownConnection(conn, ex);
308             if (this.eventListener != null) {
309                 this.eventListener.fatalIOException(ex, conn);
310             }
311         }
312 
313     }
314 
315     public void responseReady(final NHttpServerConnection conn) {
316         final HttpContext context = conn.getContext();
317 
318         final ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
319 
320         try {
321 
322             synchronized (connState) {
323                 if (connState.isExpectationFailed()) {
324                     // Server expection failed
325                     // Well-behaved client will not be sending
326                     // a request body
327                     conn.resetInput();
328                     connState.setExpectationFailed(false);
329                 }
330 
331                 final HttpResponse response = connState.getResponse();
332                 if (connState.getOutputState() == ServerConnState.READY
333                         && response != null
334                         && !conn.isResponseSubmitted()) {
335 
336                     conn.submitResponse(response);
337                     final int statusCode = response.getStatusLine().getStatusCode();
338                     final HttpEntity entity = response.getEntity();
339 
340                     if (statusCode >= 200 && entity == null) {
341                         connState.setOutputState(ServerConnState.RESPONSE_DONE);
342 
343                         if (!this.connStrategy.keepAlive(response, context)) {
344                             conn.close();
345                         }
346                     } else {
347                         connState.setOutputState(ServerConnState.RESPONSE_SENT);
348                     }
349                 }
350 
351                 connState.notifyAll();
352             }
353 
354         } catch (final IOException ex) {
355             shutdownConnection(conn, ex);
356             if (eventListener != null) {
357                 eventListener.fatalIOException(ex, conn);
358             }
359         } catch (final HttpException ex) {
360             closeConnection(conn, ex);
361             if (eventListener != null) {
362                 eventListener.fatalProtocolException(ex, conn);
363             }
364         }
365     }
366 
367     public void outputReady(final NHttpServerConnection conn, final ContentEncoder encoder) {
368         final HttpContext context = conn.getContext();
369 
370         final ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
371 
372         try {
373 
374             synchronized (connState) {
375                 final HttpResponse response = connState.getResponse();
376                 final ContentOutputBuffer buffer = connState.getOutbuffer();
377 
378                 buffer.produceContent(encoder);
379                 if (encoder.isCompleted()) {
380                     connState.setOutputState(ServerConnState.RESPONSE_BODY_DONE);
381 
382                     if (!this.connStrategy.keepAlive(response, context)) {
383                         conn.close();
384                     }
385                 } else {
386                     connState.setOutputState(ServerConnState.RESPONSE_BODY_STREAM);
387                 }
388 
389                 connState.notifyAll();
390             }
391 
392         } catch (final IOException ex) {
393             shutdownConnection(conn, ex);
394             if (this.eventListener != null) {
395                 this.eventListener.fatalIOException(ex, conn);
396             }
397         }
398     }
399 
400     private void handleException(final HttpException ex, final HttpResponse response) {
401         if (ex instanceof MethodNotSupportedException) {
402             response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
403         } else if (ex instanceof UnsupportedHttpVersionException) {
404             response.setStatusCode(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED);
405         } else if (ex instanceof ProtocolException) {
406             response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
407         } else {
408             response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
409         }
410         final byte[] msg = EncodingUtils.getAsciiBytes(ex.getMessage());
411         final ByteArrayEntity entity = new ByteArrayEntity(msg);
412         entity.setContentType("text/plain; charset=US-ASCII");
413         response.setEntity(entity);
414     }
415 
416     private void handleRequest(
417             final HttpRequest request,
418             final ServerConnState connState,
419             final NHttpServerConnection conn) throws HttpException, IOException {
420 
421         final HttpContext context = conn.getContext();
422 
423         // Block until previous request is fully processed and
424         // the worker thread no longer holds the shared buffer
425         synchronized (connState) {
426             try {
427                 for (;;) {
428                     final int currentState = connState.getOutputState();
429                     if (currentState == ServerConnState.READY) {
430                         break;
431                     }
432                     if (currentState == ServerConnState.SHUTDOWN) {
433                         return;
434                     }
435                     connState.wait();
436                 }
437             } catch (final InterruptedException ex) {
438                 connState.shutdown();
439                 return;
440             }
441             connState.setInputState(ServerConnState.REQUEST_RECEIVED);
442             connState.setRequest(request);
443         }
444 
445         request.setParams(new DefaultedHttpParams(request.getParams(), this.params));
446 
447         context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
448         context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
449 
450         ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
451 
452         if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
453             // Downgrade protocol version if greater than HTTP/1.1
454             ver = HttpVersion.HTTP_1_1;
455         }
456 
457         HttpResponse response = null;
458 
459         if (request instanceof HttpEntityEnclosingRequest) {
460             final HttpEntityEnclosingRequest eeRequest = (HttpEntityEnclosingRequest) request;
461 
462             if (eeRequest.expectContinue()) {
463                 response = this.responseFactory.newHttpResponse(
464                         ver,
465                         HttpStatus.SC_CONTINUE,
466                         context);
467                 response.setParams(
468                         new DefaultedHttpParams(response.getParams(), this.params));
469                 if (this.expectationVerifier != null) {
470                     try {
471                         this.expectationVerifier.verify(request, response, context);
472                     } catch (final HttpException ex) {
473                         response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0,
474                                 HttpStatus.SC_INTERNAL_SERVER_ERROR, context);
475                         response.setParams(
476                                 new DefaultedHttpParams(response.getParams(), this.params));
477                         handleException(ex, response);
478                     }
479                 }
480 
481                 synchronized (connState) {
482                     if (response.getStatusLine().getStatusCode() < 200) {
483                         // Send 1xx response indicating the server expections
484                         // have been met
485                         connState.setResponse(response);
486                         conn.requestOutput();
487 
488                         // Block until 1xx response is sent to the client
489                         try {
490                             for (;;) {
491                                 final int currentState = connState.getOutputState();
492                                 if (currentState == ServerConnState.RESPONSE_SENT) {
493                                     break;
494                                 }
495                                 if (currentState == ServerConnState.SHUTDOWN) {
496                                     return;
497                                 }
498                                 connState.wait();
499                             }
500                         } catch (final InterruptedException ex) {
501                             connState.shutdown();
502                             return;
503                         }
504                         connState.resetOutput();
505                         response = null;
506                     } else {
507                         // Discard request entity
508                         eeRequest.setEntity(null);
509                         conn.suspendInput();
510                         connState.setExpectationFailed(true);
511                     }
512                 }
513             }
514 
515             // Create a wrapper entity instead of the original one
516             if (eeRequest.getEntity() != null) {
517                 eeRequest.setEntity(new ContentBufferEntity(
518                         eeRequest.getEntity(),
519                         connState.getInbuffer()));
520             }
521 
522         }
523 
524         if (response == null) {
525             response = this.responseFactory.newHttpResponse(
526                     ver,
527                     HttpStatus.SC_OK,
528                     context);
529             response.setParams(
530                     new DefaultedHttpParams(response.getParams(), this.params));
531 
532             context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);
533 
534             try {
535 
536                 this.httpProcessor.process(request, context);
537 
538                 HttpRequestHandler handler = null;
539                 if (this.handlerResolver != null) {
540                     final String requestURI = request.getRequestLine().getUri();
541                     handler = this.handlerResolver.lookup(requestURI);
542                 }
543                 if (handler != null) {
544                     handler.handle(request, response, context);
545                 } else {
546                     response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
547                 }
548 
549             } catch (final HttpException ex) {
550                 response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0,
551                         HttpStatus.SC_INTERNAL_SERVER_ERROR, context);
552                 response.setParams(
553                         new DefaultedHttpParams(response.getParams(), this.params));
554                 handleException(ex, response);
555             }
556         }
557 
558         if (request instanceof HttpEntityEnclosingRequest) {
559             final HttpEntityEnclosingRequest eeRequest = (HttpEntityEnclosingRequest) request;
560             final HttpEntity entity = eeRequest.getEntity();
561             EntityUtils.consume(entity);
562         }
563 
564         // It should be safe to reset the input state at this point
565         connState.resetInput();
566 
567         this.httpProcessor.process(response, context);
568 
569         if (!canResponseHaveBody(request, response)) {
570             response.setEntity(null);
571         }
572 
573         connState.setResponse(response);
574         // Response is ready to be committed
575         conn.requestOutput();
576 
577         if (response.getEntity() != null) {
578             final ContentOutputBuffer buffer = connState.getOutbuffer();
579             final OutputStream outstream = new ContentOutputStream(buffer);
580 
581             final HttpEntity entity = response.getEntity();
582             entity.writeTo(outstream);
583             outstream.flush();
584             outstream.close();
585         }
586 
587         synchronized (connState) {
588             try {
589                 for (;;) {
590                     final int currentState = connState.getOutputState();
591                     if (currentState == ServerConnState.RESPONSE_DONE) {
592                         break;
593                     }
594                     if (currentState == ServerConnState.SHUTDOWN) {
595                         return;
596                     }
597                     connState.wait();
598                 }
599             } catch (final InterruptedException ex) {
600                 connState.shutdown();
601                 return;
602             }
603             connState.resetOutput();
604             conn.requestInput();
605             connState.notifyAll();
606         }
607     }
608 
609     @Override
610     protected void shutdownConnection(final NHttpConnection conn, final Throwable cause) {
611         final HttpContext context = conn.getContext();
612 
613         final ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
614 
615         super.shutdownConnection(conn, cause);
616 
617         if (connState != null) {
618             connState.shutdown();
619         }
620     }
621 
622     static class ServerConnState {
623 
624         public static final int SHUTDOWN                   = -1;
625         public static final int READY                      = 0;
626         public static final int REQUEST_RECEIVED           = 1;
627         public static final int REQUEST_BODY_STREAM        = 2;
628         public static final int REQUEST_BODY_DONE          = 4;
629         public static final int RESPONSE_SENT              = 8;
630         public static final int RESPONSE_BODY_STREAM       = 16;
631         public static final int RESPONSE_BODY_DONE         = 32;
632         public static final int RESPONSE_DONE              = 32;
633 
634         private final SharedInputBuffer inbuffer;
635         private final SharedOutputBuffer outbuffer;
636 
637         private volatile int inputState;
638         private volatile int outputState;
639 
640         private volatile HttpRequest request;
641         private volatile HttpResponse response;
642 
643         private volatile boolean expectationFailure;
644 
645         public ServerConnState(
646                 final int bufsize,
647                 final IOControl ioControl,
648                 final ByteBufferAllocator allocator) {
649             super();
650             this.inbuffer = new SharedInputBuffer(bufsize, ioControl, allocator);
651             this.outbuffer = new SharedOutputBuffer(bufsize, ioControl, allocator);
652             this.inputState = READY;
653             this.outputState = READY;
654         }
655 
656         public ContentInputBuffer getInbuffer() {
657             return this.inbuffer;
658         }
659 
660         public ContentOutputBuffer getOutbuffer() {
661             return this.outbuffer;
662         }
663 
664         public int getInputState() {
665             return this.inputState;
666         }
667 
668         public void setInputState(final int inputState) {
669             this.inputState = inputState;
670         }
671 
672         public int getOutputState() {
673             return this.outputState;
674         }
675 
676         public void setOutputState(final int outputState) {
677             this.outputState = outputState;
678         }
679 
680         public HttpRequest getRequest() {
681             return this.request;
682         }
683 
684         public void setRequest(final HttpRequest request) {
685             this.request = request;
686         }
687 
688         public HttpResponse getResponse() {
689             return this.response;
690         }
691 
692         public void setResponse(final HttpResponse response) {
693             this.response = response;
694         }
695 
696         public boolean isExpectationFailed() {
697             return expectationFailure;
698         }
699 
700         public void setExpectationFailed(final boolean b) {
701             this.expectationFailure = b;
702         }
703 
704         public void close() {
705             this.inbuffer.close();
706             this.outbuffer.close();
707             this.inputState = SHUTDOWN;
708             this.outputState = SHUTDOWN;
709         }
710 
711         public void shutdown() {
712             this.inbuffer.shutdown();
713             this.outbuffer.shutdown();
714             this.inputState = SHUTDOWN;
715             this.outputState = SHUTDOWN;
716         }
717 
718         public void resetInput() {
719             this.inbuffer.reset();
720             this.request = null;
721             this.inputState = READY;
722         }
723 
724         public void resetOutput() {
725             this.outbuffer.reset();
726             this.response = null;
727             this.outputState = READY;
728             this.expectationFailure = false;
729         }
730 
731     }
732 
733 }