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                 incomingContentStrategy != null ? incomingContentStrategy :
146                     DisallowIdentityContentLengthStrategy.INSTANCE,
147                 outgoingContentStrategy != null ? outgoingContentStrategy :
148                     StrictContentLengthStrategy.INSTANCE);
149         this.requestParser = (requestParserFactory != null ? requestParserFactory :
150             DefaultHttpRequestParserFactory.INSTANCE).create(this.inbuf, constraints);
151         this.responseWriter = (responseWriterFactory != null ? responseWriterFactory :
152             DefaultHttpResponseWriterFactory.INSTANCE).create(this.outbuf);
153     }
154 
155     /**
156      * @since 4.3
157      */
158     public DefaultNHttpServerConnection(
159             final IOSession session,
160             final int buffersize,
161             final CharsetDecoder chardecoder,
162             final CharsetEncoder charencoder,
163             final MessageConstraints constraints) {
164         this(session, buffersize, buffersize, null, chardecoder, charencoder, constraints,
165                 null, null, null, null);
166     }
167 
168     /**
169      * @since 4.3
170      */
171     public DefaultNHttpServerConnection(final IOSession session, final int buffersize) {
172         this(session, buffersize, buffersize, null, null, null, null, null, null, null, null);
173     }
174 
175     /**
176      * @deprecated (4.3) use constructor.
177      */
178     @Override
179     @Deprecated
180     protected ContentLengthStrategy createIncomingContentStrategy() {
181         return new DisallowIdentityContentLengthStrategy(new LaxContentLengthStrategy(0));
182     }
183 
184     /**
185      * Creates an instance of {@link NHttpMessageParser} to be used
186      * by this connection for parsing incoming {@link HttpRequest} messages.
187      * <p>
188      * This method can be overridden in a super class in order to provide
189      * a different implementation of the {@link NHttpMessageParser} interface.
190      *
191      * @return HTTP response parser.
192      *
193      * @deprecated (4.3) use constructor.
194      */
195     @Deprecated
196     protected NHttpMessageParser<HttpRequest> createRequestParser(
197             final SessionInputBuffer buffer,
198             final HttpRequestFactory requestFactory,
199             final HttpParams params) {
200         final MessageConstraints constraints = HttpParamConfig.getMessageConstraints(params);
201         return new DefaultHttpRequestParser(buffer, null, requestFactory, constraints);
202     }
203 
204     /**
205      * Creates an instance of {@link NHttpMessageWriter} to be used
206      * by this connection for writing out outgoing {@link HttpResponse}
207      * messages.
208      * <p>
209      * This method can be overridden by a super class in order to provide
210      * a different implementation of the {@link NHttpMessageWriter} interface.
211      *
212      * @return HTTP response parser.
213      *
214      * @deprecated (4.3) use constructor.
215      */
216     @Deprecated
217     protected NHttpMessageWriter<HttpResponse> createResponseWriter(
218             final SessionOutputBuffer buffer,
219             final HttpParams params) {
220         // override in derived class to specify a line formatter
221         return new DefaultHttpResponseWriter(buffer, null);
222     }
223 
224     /**
225      * @since 4.2
226      */
227     protected void onRequestReceived(final HttpRequest request) {
228     }
229 
230     /**
231      * @since 4.2
232      */
233     protected void onResponseSubmitted(final HttpResponse response) {
234     }
235 
236     public void resetInput() {
237         this.request = null;
238         this.contentDecoder = null;
239         this.requestParser.reset();
240     }
241 
242     public void resetOutput() {
243         this.response = null;
244         this.contentEncoder = null;
245         this.responseWriter.reset();
246     }
247 
248     public void consumeInput(final NHttpServerEventHandler handler) {
249         if (this.status != ACTIVE) {
250             this.session.clearEvent(EventMask.READ);
251             return;
252         }
253         try {
254             if (this.request == null) {
255                 int bytesRead;
256                 do {
257                     bytesRead = this.requestParser.fillBuffer(this.session.channel());
258                     if (bytesRead > 0) {
259                         this.inTransportMetrics.incrementBytesTransferred(bytesRead);
260                     }
261                     this.request = this.requestParser.parse();
262                 } while (bytesRead > 0 && this.request == null);
263                 if (this.request != null) {
264                     if (this.request instanceof HttpEntityEnclosingRequest) {
265                         // Receive incoming entity
266                         final HttpEntity entity = prepareDecoder(this.request);
267                         ((HttpEntityEnclosingRequest)this.request).setEntity(entity);
268                     }
269                     this.connMetrics.incrementRequestCount();
270                     onRequestReceived(this.request);
271                     handler.requestReceived(this);
272                     if (this.contentDecoder == null) {
273                         // No request entity is expected
274                         // Ready to receive a new request
275                         resetInput();
276                     }
277                 }
278                 if (bytesRead == -1) {
279                     handler.endOfInput(this);
280                 }
281             }
282             if (this.contentDecoder != null && (this.session.getEventMask() & SelectionKey.OP_READ) > 0) {
283                 handler.inputReady(this, this.contentDecoder);
284                 if (this.contentDecoder.isCompleted()) {
285                     // Request entity received
286                     // Ready to receive a new request
287                     resetInput();
288                 }
289             }
290         } catch (final HttpException ex) {
291             resetInput();
292             handler.exception(this, ex);
293         } catch (final Exception ex) {
294             handler.exception(this, ex);
295         } finally {
296             // Finally set buffered input flag
297             this.hasBufferedInput = this.inbuf.hasData();
298         }
299     }
300 
301     public void produceOutput(final NHttpServerEventHandler handler) {
302         try {
303             if (this.status == ACTIVE) {
304                 if (this.contentEncoder == null) {
305                     handler.responseReady(this);
306                 }
307                 if (this.contentEncoder != null) {
308                     handler.outputReady(this, this.contentEncoder);
309                     if (this.contentEncoder.isCompleted()) {
310                         resetOutput();
311                     }
312                 }
313             }
314             if (this.outbuf.hasData()) {
315                 final int bytesWritten = this.outbuf.flush(this.session.channel());
316                 if (bytesWritten > 0) {
317                     this.outTransportMetrics.incrementBytesTransferred(bytesWritten);
318                 }
319             }
320             if (!this.outbuf.hasData()) {
321                 if (this.status == CLOSING) {
322                     this.session.close();
323                     this.status = CLOSED;
324                     resetOutput();
325                 }
326                 if (this.contentEncoder == null && this.status != CLOSED) {
327                     this.session.clearEvent(EventMask.WRITE);
328                 }
329             }
330         } catch (final Exception ex) {
331             handler.exception(this, ex);
332         } finally {
333             // Finally set the buffered output flag
334             this.hasBufferedOutput = this.outbuf.hasData();
335         }
336     }
337 
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     public boolean isResponseSubmitted() {
360         return this.response != null;
361     }
362 
363     public void consumeInput(final NHttpServiceHandler handler) {
364         consumeInput(new NHttpServerEventHandlerAdaptor(handler));
365     }
366 
367     public void produceOutput(final NHttpServiceHandler handler) {
368         produceOutput(new NHttpServerEventHandlerAdaptor(handler));
369     }
370 
371 }