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