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.impl.nio;
29  
30  import java.io.IOException;
31  import java.nio.channels.SelectionKey;
32  import java.nio.charset.CharsetDecoder;
33  import java.nio.charset.CharsetEncoder;
34  
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.HttpRequestFactory;
40  import org.apache.http.HttpResponse;
41  import org.apache.http.annotation.NotThreadSafe;
42  import org.apache.http.config.MessageConstraints;
43  import org.apache.http.entity.ContentLengthStrategy;
44  import org.apache.http.impl.entity.DisallowIdentityContentLengthStrategy;
45  import org.apache.http.impl.entity.LaxContentLengthStrategy;
46  import org.apache.http.impl.entity.StrictContentLengthStrategy;
47  import org.apache.http.impl.nio.codecs.DefaultHttpRequestParser;
48  import org.apache.http.impl.nio.codecs.DefaultHttpRequestParserFactory;
49  import org.apache.http.impl.nio.codecs.DefaultHttpResponseWriter;
50  import org.apache.http.impl.nio.codecs.DefaultHttpResponseWriterFactory;
51  import org.apache.http.nio.NHttpMessageParser;
52  import org.apache.http.nio.NHttpMessageParserFactory;
53  import org.apache.http.nio.NHttpMessageWriter;
54  import org.apache.http.nio.NHttpMessageWriterFactory;
55  import org.apache.http.nio.NHttpServerEventHandler;
56  import org.apache.http.nio.NHttpServerIOTarget;
57  import org.apache.http.nio.NHttpServiceHandler;
58  import org.apache.http.nio.reactor.EventMask;
59  import org.apache.http.nio.reactor.IOSession;
60  import org.apache.http.nio.reactor.SessionInputBuffer;
61  import org.apache.http.nio.reactor.SessionOutputBuffer;
62  import org.apache.http.nio.util.ByteBufferAllocator;
63  import org.apache.http.params.HttpParamConfig;
64  import org.apache.http.params.HttpParams;
65  import org.apache.http.util.Args;
66  
67  /**
68   * Default implementation of the {@link org.apache.http.nio.NHttpServerConnection}
69   * interface.
70   *
71   * @since 4.0
72   */
73  @SuppressWarnings("deprecation")
74  @NotThreadSafe
75  public class DefaultNHttpServerConnection
76      extends NHttpConnectionBase implements NHttpServerIOTarget {
77  
78      protected final NHttpMessageParser<HttpRequest> requestParser;
79      protected final NHttpMessageWriter<HttpResponse> responseWriter;
80  
81      /**
82       * Creates a new instance of this class given the underlying I/O session.
83       *
84       * @param session the underlying I/O session.
85       * @param requestFactory HTTP request factory.
86       * @param allocator byte buffer allocator.
87       * @param params HTTP parameters.
88       *
89       * @deprecated (4.3) use {@link DefaultNHttpServerConnection#DefaultNHttpServerConnection(
90       *   IOSession, int, int, ByteBufferAllocator, CharsetDecoder, CharsetEncoder,
91       *   MessageConstraints, ContentLengthStrategy, ContentLengthStrategy,
92       *   NHttpMessageParserFactory, NHttpMessageWriterFactory)}
93       */
94      @Deprecated
95      public DefaultNHttpServerConnection(
96              final IOSession session,
97              final HttpRequestFactory requestFactory,
98              final ByteBufferAllocator allocator,
99              final HttpParams params) {
100         super(session, allocator, params);
101         Args.notNull(requestFactory, "Request factory");
102         this.requestParser = createRequestParser(this.inbuf, requestFactory, params);
103         this.responseWriter = createResponseWriter(this.outbuf, params);
104     }
105 
106     /**
107      * Creates new instance DefaultNHttpServerConnection given the underlying I/O session.
108      *
109      * @param session the underlying I/O session.
110      * @param buffersize buffer size. Must be a positive number.
111      * @param fragmentSizeHint fragment size hint.
112      * @param allocator memory allocator.
113      *   If <code>null</code> {@link org.apache.http.nio.util.HeapByteBufferAllocator#INSTANCE}
114      *   will be used.
115      * @param chardecoder decoder to be used for decoding HTTP protocol elements.
116      *   If <code>null</code> simple type cast will be used for byte to char conversion.
117      * @param charencoder encoder to be used for encoding HTTP protocol elements.
118      *   If <code>null</code> simple type cast will be used for char to byte conversion.
119      * @param constraints Message constraints. If <code>null</code>
120      *   {@link MessageConstraints#DEFAULT} will be used.
121      * @param incomingContentStrategy incoming content length strategy. If <code>null</code>
122      *   {@link DisallowIdentityContentLengthStrategy#INSTANCE} will be used.
123      * @param outgoingContentStrategy outgoing content length strategy. If <code>null</code>
124      *   {@link StrictContentLengthStrategy#INSTANCE} will be used.
125      * @param requestParserFactory request parser factory. If <code>null</code>
126      *   {@link DefaultHttpRequestParserFactory#INSTANCE} will be used.
127      * @param responseWriterFactory response writer factory. If <code>null</code>
128      *   {@link DefaultHttpResponseWriterFactory#INSTANCE} will be used.
129      *
130      * @since 4.3
131      */
132     public DefaultNHttpServerConnection(
133             final IOSession session,
134             final int buffersize,
135             final int fragmentSizeHint,
136             final ByteBufferAllocator allocator,
137             final CharsetDecoder chardecoder,
138             final CharsetEncoder charencoder,
139             final MessageConstraints constraints,
140             final ContentLengthStrategy incomingContentStrategy,
141             final ContentLengthStrategy outgoingContentStrategy,
142             final NHttpMessageParserFactory<HttpRequest> requestParserFactory,
143             final NHttpMessageWriterFactory<HttpResponse> responseWriterFactory) {
144         super(session, buffersize, fragmentSizeHint, allocator, chardecoder, charencoder,
145                 constraints,
146                 incomingContentStrategy != null ? incomingContentStrategy :
147                     DisallowIdentityContentLengthStrategy.INSTANCE,
148                 outgoingContentStrategy != null ? outgoingContentStrategy :
149                     StrictContentLengthStrategy.INSTANCE);
150         this.requestParser = (requestParserFactory != null ? requestParserFactory :
151             DefaultHttpRequestParserFactory.INSTANCE).create(this.inbuf, constraints);
152         this.responseWriter = (responseWriterFactory != null ? responseWriterFactory :
153             DefaultHttpResponseWriterFactory.INSTANCE).create(this.outbuf);
154     }
155 
156     /**
157      * @since 4.3
158      */
159     public DefaultNHttpServerConnection(
160             final IOSession session,
161             final int buffersize,
162             final CharsetDecoder chardecoder,
163             final CharsetEncoder charencoder,
164             final MessageConstraints constraints) {
165         this(session, buffersize, buffersize, null, chardecoder, charencoder, constraints,
166                 null, null, null, null);
167     }
168 
169     /**
170      * @since 4.3
171      */
172     public DefaultNHttpServerConnection(final IOSession session, final int buffersize) {
173         this(session, buffersize, buffersize, null, null, null, null, null, null, null, null);
174     }
175 
176     /**
177      * @deprecated (4.3) use constructor.
178      */
179     @Override
180     @Deprecated
181     protected ContentLengthStrategy createIncomingContentStrategy() {
182         return new DisallowIdentityContentLengthStrategy(new LaxContentLengthStrategy(0));
183     }
184 
185     /**
186      * Creates an instance of {@link NHttpMessageParser} to be used
187      * by this connection for parsing incoming {@link HttpRequest} messages.
188      * <p>
189      * This method can be overridden in a super class in order to provide
190      * a different implementation of the {@link NHttpMessageParser} interface.
191      *
192      * @return HTTP response parser.
193      *
194      * @deprecated (4.3) use constructor.
195      */
196     @Deprecated
197     protected NHttpMessageParser<HttpRequest> createRequestParser(
198             final SessionInputBuffer buffer,
199             final HttpRequestFactory requestFactory,
200             final HttpParams params) {
201         final MessageConstraints constraints = HttpParamConfig.getMessageConstraints(params);
202         return new DefaultHttpRequestParser(buffer, null, requestFactory, constraints);
203     }
204 
205     /**
206      * Creates an instance of {@link NHttpMessageWriter} to be used
207      * by this connection for writing out outgoing {@link HttpResponse}
208      * messages.
209      * <p>
210      * This method can be overridden by a super class in order to provide
211      * a different implementation of the {@link NHttpMessageWriter} interface.
212      *
213      * @return HTTP response parser.
214      *
215      * @deprecated (4.3) use constructor.
216      */
217     @Deprecated
218     protected NHttpMessageWriter<HttpResponse> createResponseWriter(
219             final SessionOutputBuffer buffer,
220             final HttpParams params) {
221         // override in derived class to specify a line formatter
222         return new DefaultHttpResponseWriter(buffer, null);
223     }
224 
225     /**
226      * @since 4.2
227      */
228     protected void onRequestReceived(final HttpRequest request) {
229     }
230 
231     /**
232      * @since 4.2
233      */
234     protected void onResponseSubmitted(final HttpResponse response) {
235     }
236 
237     @Override
238     public void resetInput() {
239         this.request = null;
240         this.contentDecoder = null;
241         this.requestParser.reset();
242     }
243 
244     @Override
245     public void resetOutput() {
246         this.response = null;
247         this.contentEncoder = null;
248         this.responseWriter.reset();
249     }
250 
251     public void consumeInput(final NHttpServerEventHandler handler) {
252         if (this.status != ACTIVE) {
253             this.session.clearEvent(EventMask.READ);
254             return;
255         }
256         try {
257             if (this.request == null) {
258                 int bytesRead;
259                 do {
260                     bytesRead = this.requestParser.fillBuffer(this.session.channel());
261                     if (bytesRead > 0) {
262                         this.inTransportMetrics.incrementBytesTransferred(bytesRead);
263                     }
264                     this.request = this.requestParser.parse();
265                 } while (bytesRead > 0 && this.request == null);
266                 if (this.request != null) {
267                     if (this.request instanceof HttpEntityEnclosingRequest) {
268                         // Receive incoming entity
269                         final HttpEntity entity = prepareDecoder(this.request);
270                         ((HttpEntityEnclosingRequest)this.request).setEntity(entity);
271                     }
272                     this.connMetrics.incrementRequestCount();
273                     this.hasBufferedInput = this.inbuf.hasData();
274                     onRequestReceived(this.request);
275                     handler.requestReceived(this);
276                     if (this.contentDecoder == null) {
277                         // No request entity is expected
278                         // Ready to receive a new request
279                         resetInput();
280                     }
281                 }
282                 if (bytesRead == -1) {
283                     handler.endOfInput(this);
284                 }
285             }
286             if (this.contentDecoder != null && (this.session.getEventMask() & SelectionKey.OP_READ) > 0) {
287                 handler.inputReady(this, this.contentDecoder);
288                 if (this.contentDecoder.isCompleted()) {
289                     // Request entity received
290                     // Ready to receive a new request
291                     resetInput();
292                 }
293             }
294         } catch (final HttpException ex) {
295             resetInput();
296             handler.exception(this, ex);
297         } catch (final Exception ex) {
298             handler.exception(this, ex);
299         } finally {
300             // Finally set buffered input flag
301             this.hasBufferedInput = this.inbuf.hasData();
302         }
303     }
304 
305     public void produceOutput(final NHttpServerEventHandler handler) {
306         try {
307             if (this.status == ACTIVE) {
308                 if (this.contentEncoder == null) {
309                     handler.responseReady(this);
310                 }
311                 if (this.contentEncoder != null) {
312                     handler.outputReady(this, this.contentEncoder);
313                     if (this.contentEncoder.isCompleted()) {
314                         resetOutput();
315                     }
316                 }
317             }
318             if (this.outbuf.hasData()) {
319                 final int bytesWritten = this.outbuf.flush(this.session.channel());
320                 if (bytesWritten > 0) {
321                     this.outTransportMetrics.incrementBytesTransferred(bytesWritten);
322                 }
323             }
324             if (!this.outbuf.hasData()) {
325                 if (this.status == CLOSING) {
326                     this.session.close();
327                     this.status = CLOSED;
328                     resetOutput();
329                 }
330             }
331         } catch (final Exception ex) {
332             handler.exception(this, ex);
333         } finally {
334             // Finally set the buffered output flag
335             this.hasBufferedOutput = this.outbuf.hasData();
336         }
337     }
338 
339     @Override
340     public void submitResponse(final HttpResponse response) throws IOException, HttpException {
341         Args.notNull(response, "HTTP response");
342         assertNotClosed();
343         if (this.response != null) {
344             throw new HttpException("Response already submitted");
345         }
346         onResponseSubmitted(response);
347         this.responseWriter.write(response);
348         this.hasBufferedOutput = this.outbuf.hasData();
349 
350         if (response.getStatusLine().getStatusCode() >= 200) {
351             this.connMetrics.incrementResponseCount();
352             if (response.getEntity() != null) {
353                 this.response = response;
354                 prepareEncoder(response);
355             }
356         }
357 
358         this.session.setEvent(EventMask.WRITE);
359     }
360 
361     @Override
362     public boolean isResponseSubmitted() {
363         return this.response != null;
364     }
365 
366     @Override
367     public void consumeInput(final NHttpServiceHandler handler) {
368         consumeInput(new NHttpServerEventHandlerAdaptor(handler));
369     }
370 
371     @Override
372     public void produceOutput(final NHttpServiceHandler handler) {
373         produceOutput(new NHttpServerEventHandlerAdaptor(handler));
374     }
375 
376 }