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.NHttpServerConnection;
56  import org.apache.http.nio.NHttpServerEventHandler;
57  import org.apache.http.nio.NHttpServerIOTarget;
58  import org.apache.http.nio.NHttpServiceHandler;
59  import org.apache.http.nio.reactor.EventMask;
60  import org.apache.http.nio.reactor.IOSession;
61  import org.apache.http.nio.reactor.SessionInputBuffer;
62  import org.apache.http.nio.reactor.SessionOutputBuffer;
63  import org.apache.http.nio.util.ByteBufferAllocator;
64  import org.apache.http.nio.util.HeapByteBufferAllocator;
65  import org.apache.http.params.HttpParamConfig;
66  import org.apache.http.params.HttpParams;
67  import org.apache.http.util.Args;
68  
69  /**
70   * Default implementation of the {@link NHttpServerConnection} interface.
71   *
72   * @since 4.0
73   */
74  @SuppressWarnings("deprecation")
75  @NotThreadSafe
76  public class DefaultNHttpServerConnection
77      extends NHttpConnectionBase implements NHttpServerIOTarget {
78  
79      protected final NHttpMessageParser<HttpRequest> requestParser;
80      protected final NHttpMessageWriter<HttpResponse> responseWriter;
81  
82      /**
83       * Creates a new instance of this class given the underlying I/O session.
84       *
85       * @param session the underlying I/O session.
86       * @param requestFactory HTTP request factory.
87       * @param allocator byte buffer allocator.
88       * @param params HTTP parameters.
89       *
90       * @deprecated (4.3) use {@link DefaultNHttpServerConnection#DefaultNHttpServerConnection(
91       *   IOSession, int, int, ByteBufferAllocator, CharsetDecoder, CharsetEncoder,
92       *   MessageConstraints, ContentLengthStrategy, ContentLengthStrategy,
93       *   NHttpMessageParserFactory, NHttpMessageWriterFactory)}
94       */
95      @Deprecated
96      public DefaultNHttpServerConnection(
97              final IOSession session,
98              final HttpRequestFactory requestFactory,
99              final ByteBufferAllocator allocator,
100             final HttpParams params) {
101         super(session, allocator, params);
102         Args.notNull(requestFactory, "Request factory");
103         this.requestParser = createRequestParser(this.inbuf, requestFactory, params);
104         this.responseWriter = createResponseWriter(this.outbuf, params);
105     }
106 
107     /**
108      * Creates new instance DefaultNHttpServerConnection given the underlying I/O session.
109      *
110      * @param session the underlying I/O session.
111      * @param buffersize buffer size. Must be a positive number.
112      * @param fragmentSizeHint fragment size hint.
113      * @param allocator memory allocator.
114      *   If <code>null</code> {@link HeapByteBufferAllocator#INSTANCE} 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     public void resetInput() {
238         this.request = null;
239         this.contentDecoder = null;
240         this.requestParser.reset();
241     }
242 
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                     onRequestReceived(this.request);
272                     handler.requestReceived(this);
273                     if (this.contentDecoder == null) {
274                         // No request entity is expected
275                         // Ready to receive a new request
276                         resetInput();
277                     }
278                 }
279                 if (bytesRead == -1) {
280                     handler.endOfInput(this);
281                 }
282             }
283             if (this.contentDecoder != null && (this.session.getEventMask() & SelectionKey.OP_READ) > 0) {
284                 handler.inputReady(this, this.contentDecoder);
285                 if (this.contentDecoder.isCompleted()) {
286                     // Request entity received
287                     // Ready to receive a new request
288                     resetInput();
289                 }
290             }
291         } catch (final HttpException ex) {
292             resetInput();
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) {
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                 if (this.contentEncoder == null && this.status != CLOSED) {
328                     this.session.clearEvent(EventMask.WRITE);
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     public void submitResponse(final HttpResponse response) throws IOException, HttpException {
340         Args.notNull(response, "HTTP response");
341         assertNotClosed();
342         if (this.response != null) {
343             throw new HttpException("Response already submitted");
344         }
345         onResponseSubmitted(response);
346         this.responseWriter.write(response);
347         this.hasBufferedOutput = this.outbuf.hasData();
348 
349         if (response.getStatusLine().getStatusCode() >= 200) {
350             this.connMetrics.incrementResponseCount();
351             if (response.getEntity() != null) {
352                 this.response = response;
353                 prepareEncoder(response);
354             }
355         }
356 
357         this.session.setEvent(EventMask.WRITE);
358     }
359 
360     public boolean isResponseSubmitted() {
361         return this.response != null;
362     }
363 
364     public void consumeInput(final NHttpServiceHandler handler) {
365         consumeInput(new NHttpServerEventHandlerAdaptor(handler));
366     }
367 
368     public void produceOutput(final NHttpServiceHandler handler) {
369         produceOutput(new NHttpServerEventHandlerAdaptor(handler));
370     }
371 
372 }