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             resetInput();
294             handler.exception(this, ex);
295         } catch (final Exception ex) {
296             handler.exception(this, ex);
297         } finally {
298             // Finally set buffered input flag
299             this.hasBufferedInput = this.inbuf.hasData();
300         }
301     }
302 
303     public void produceOutput(final NHttpServerEventHandler handler) {
304         try {
305             if (this.status == ACTIVE) {
306                 if (this.contentEncoder == null && !this.outbuf.hasData()) {
307                     handler.responseReady(this);
308                 }
309                 if (this.contentEncoder != null) {
310                     handler.outputReady(this, this.contentEncoder);
311                     if (this.contentEncoder.isCompleted()) {
312                         resetOutput();
313                     }
314                 }
315             }
316             if (this.outbuf.hasData()) {
317                 final int bytesWritten = this.outbuf.flush(this.session.channel());
318                 if (bytesWritten > 0) {
319                     this.outTransportMetrics.incrementBytesTransferred(bytesWritten);
320                 }
321             }
322             if (!this.outbuf.hasData()) {
323                 if (this.status == CLOSING) {
324                     this.session.close();
325                     this.status = CLOSED;
326                     resetOutput();
327                 }
328             }
329         } catch (final Exception ex) {
330             handler.exception(this, ex);
331         } finally {
332             // Finally set the buffered output flag
333             this.hasBufferedOutput = this.outbuf.hasData();
334         }
335     }
336 
337     @Override
338     public void submitResponse(final HttpResponse response) throws IOException, HttpException {
339         Args.notNull(response, "HTTP response");
340         assertNotClosed();
341         if (this.response != null) {
342             throw new HttpException("Response already submitted");
343         }
344         onResponseSubmitted(response);
345         this.responseWriter.write(response);
346         this.hasBufferedOutput = this.outbuf.hasData();
347 
348         if (response.getStatusLine().getStatusCode() >= 200) {
349             this.connMetrics.incrementResponseCount();
350             if (response.getEntity() != null) {
351                 this.response = response;
352                 prepareEncoder(response);
353             }
354         }
355 
356         this.session.setEvent(EventMask.WRITE);
357     }
358 
359     @Override
360     public boolean isResponseSubmitted() {
361         return this.response != null;
362     }
363 
364     @Override
365     public void consumeInput(final NHttpServiceHandler handler) {
366         consumeInput(new NHttpServerEventHandlerAdaptor(handler));
367     }
368 
369     @Override
370     public void produceOutput(final NHttpServiceHandler handler) {
371         produceOutput(new NHttpServerEventHandlerAdaptor(handler));
372     }
373 
374 }