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  
33  import org.apache.http.HttpEntity;
34  import org.apache.http.HttpEntityEnclosingRequest;
35  import org.apache.http.HttpException;
36  import org.apache.http.HttpRequest;
37  import org.apache.http.HttpRequestFactory;
38  import org.apache.http.HttpResponse;
39  import org.apache.http.annotation.NotThreadSafe;
40  import org.apache.http.entity.ContentLengthStrategy;
41  import org.apache.http.impl.entity.DisallowIdentityContentLengthStrategy;
42  import org.apache.http.impl.entity.LaxContentLengthStrategy;
43  import org.apache.http.impl.nio.codecs.DefaultHttpRequestParser;
44  import org.apache.http.impl.nio.codecs.DefaultHttpResponseWriter;
45  import org.apache.http.nio.NHttpMessageParser;
46  import org.apache.http.nio.NHttpMessageWriter;
47  import org.apache.http.nio.NHttpServerConnection;
48  import org.apache.http.nio.NHttpServerIOTarget;
49  import org.apache.http.nio.NHttpServerEventHandler;
50  import org.apache.http.nio.NHttpServiceHandler;
51  import org.apache.http.nio.reactor.EventMask;
52  import org.apache.http.nio.reactor.IOSession;
53  import org.apache.http.nio.reactor.SessionInputBuffer;
54  import org.apache.http.nio.reactor.SessionOutputBuffer;
55  import org.apache.http.nio.util.ByteBufferAllocator;
56  import org.apache.http.params.HttpParams;
57  
58  /**
59   * Default implementation of the {@link NHttpServerConnection} interface.
60   * <p>
61   * The following parameters can be used to customize the behavior of this
62   * class:
63   * <ul>
64   *  <li>{@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
65   *  <li>{@link org.apache.http.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li>
66   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
67   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
68   * </ul>
69   *
70   * @since 4.0
71   */
72  @SuppressWarnings("deprecation")
73  @NotThreadSafe
74  public class DefaultNHttpServerConnection
75      extends NHttpConnectionBase implements NHttpServerIOTarget {
76  
77      protected final NHttpMessageParser<HttpRequest> requestParser;
78      protected final NHttpMessageWriter<HttpResponse> responseWriter;
79  
80      /**
81       * Creates a new instance of this class given the underlying I/O session.
82       *
83       * @param session the underlying I/O session.
84       * @param requestFactory HTTP request factory.
85       * @param allocator byte buffer allocator.
86       * @param params HTTP parameters.
87       */
88      public DefaultNHttpServerConnection(
89              final IOSession session,
90              final HttpRequestFactory requestFactory,
91              final ByteBufferAllocator allocator,
92              final HttpParams params) {
93          super(session, allocator, params);
94          if (requestFactory == null) {
95              throw new IllegalArgumentException("Request factory may not be null");
96          }
97          this.requestParser = createRequestParser(this.inbuf, requestFactory, params);
98          this.responseWriter = createResponseWriter(this.outbuf, params);
99      }
100 
101     @Override
102     protected ContentLengthStrategy createIncomingContentStrategy() {
103         return new DisallowIdentityContentLengthStrategy(new LaxContentLengthStrategy(0));
104     }
105 
106     /**
107      * Creates an instance of {@link NHttpMessageParser} to be used
108      * by this connection for parsing incoming {@link HttpRequest} messages.
109      * <p>
110      * This method can be overridden in a super class in order to provide
111      * a different implementation of the {@link NHttpMessageParser} interface.
112      *
113      * @return HTTP response parser.
114      */
115     protected NHttpMessageParser<HttpRequest> createRequestParser(
116             final SessionInputBuffer buffer,
117             final HttpRequestFactory requestFactory,
118             final HttpParams params) {
119         // override in derived class to specify a line parser
120         return new DefaultHttpRequestParser(buffer, null, requestFactory, params);
121     }
122 
123     /**
124      * Creates an instance of {@link NHttpMessageWriter} to be used
125      * by this connection for writing out outgoing {@link HttpResponse}
126      * messages.
127      * <p>
128      * This method can be overridden by a super class in order to provide
129      * a different implementation of the {@link NHttpMessageWriter} interface.
130      *
131      * @return HTTP response parser.
132      */
133     protected NHttpMessageWriter<HttpResponse> createResponseWriter(
134             final SessionOutputBuffer buffer,
135             final HttpParams params) {
136         // override in derived class to specify a line formatter
137         return new DefaultHttpResponseWriter(buffer, null, params);
138     }
139 
140     /**
141      * @since 4.2
142      */
143     protected void onRequestReceived(final HttpRequest request) {
144     }
145     
146     /**
147      * @since 4.2
148      */
149     protected void onResponseSubmitted(final HttpResponse response) {
150     }
151     
152     public void resetInput() {
153         this.request = null;
154         this.contentDecoder = null;
155         this.requestParser.reset();
156     }
157 
158     public void resetOutput() {
159         this.response = null;
160         this.contentEncoder = null;
161         this.responseWriter.reset();
162     }
163 
164     public void consumeInput(final NHttpServerEventHandler handler) {
165         if (this.status != ACTIVE) {
166             this.session.clearEvent(EventMask.READ);
167             return;
168         }
169         try {
170             if (this.request == null) {
171                 int bytesRead;
172                 do {
173                     bytesRead = this.requestParser.fillBuffer(this.session.channel());
174                     if (bytesRead > 0) {
175                         this.inTransportMetrics.incrementBytesTransferred(bytesRead);
176                     }
177                     this.request = this.requestParser.parse();
178                 } while (bytesRead > 0 && this.request == null);
179                 if (this.request != null) {
180                     if (this.request instanceof HttpEntityEnclosingRequest) {
181                         // Receive incoming entity
182                         HttpEntity entity = prepareDecoder(this.request);
183                         ((HttpEntityEnclosingRequest)this.request).setEntity(entity);
184                     }
185                     this.connMetrics.incrementRequestCount();
186                     onRequestReceived(this.request);
187                     handler.requestReceived(this);
188                     if (this.contentDecoder == null) {
189                         // No request entity is expected
190                         // Ready to receive a new request
191                         resetInput();
192                     }
193                 }
194                 if (bytesRead == -1) {
195                     handler.endOfInput(this);
196                 }
197             }
198             if (this.contentDecoder != null && (this.session.getEventMask() & SelectionKey.OP_READ) > 0) {
199                 handler.inputReady(this, this.contentDecoder);
200                 if (this.contentDecoder.isCompleted()) {
201                     // Request entity received
202                     // Ready to receive a new request
203                     resetInput();
204                 }
205             }
206         } catch (HttpException ex) {
207             resetInput();
208             handler.exception(this, ex);
209         } catch (Exception ex) {
210             handler.exception(this, ex);
211         } finally {
212             // Finally set buffered input flag
213             this.hasBufferedInput = this.inbuf.hasData();
214         }
215     }
216 
217     public void produceOutput(final NHttpServerEventHandler handler) {
218         try {
219             if (this.outbuf.hasData()) {
220                 int bytesWritten = this.outbuf.flush(this.session.channel());
221                 if (bytesWritten > 0) {
222                     this.outTransportMetrics.incrementBytesTransferred(bytesWritten);
223                 }
224             }
225             if (!this.outbuf.hasData()) {
226                 if (this.status == CLOSING) {
227                     this.session.close();
228                     this.status = CLOSED;
229                     resetOutput();
230                     return;
231                 } else {
232                     if (this.contentEncoder != null) {
233                         handler.outputReady(this, this.contentEncoder);
234                         if (this.contentEncoder.isCompleted()) {
235                             resetOutput();
236                         }
237                     }
238                 }
239 
240                 if (this.contentEncoder == null && !this.outbuf.hasData()) {
241                     if (this.status == CLOSING) {
242                         this.session.close();
243                         this.status = CLOSED;
244                     }
245                     if (this.status != CLOSED) {
246                         this.session.clearEvent(EventMask.WRITE);
247                         handler.responseReady(this);
248                     }
249                 }
250             }
251         } catch (Exception ex) {
252             handler.exception(this, ex);
253         } finally {
254             // Finally set the buffered output flag
255             this.hasBufferedOutput = this.outbuf.hasData();
256         }
257     }
258 
259     public void submitResponse(final HttpResponse response) throws IOException, HttpException {
260         if (response == null) {
261             throw new IllegalArgumentException("HTTP response may not be null");
262         }
263         assertNotClosed();
264         if (this.response != null) {
265             throw new HttpException("Response already submitted");
266         }
267         onResponseSubmitted(response);
268         this.responseWriter.write(response);
269         this.hasBufferedOutput = this.outbuf.hasData();
270 
271         if (response.getStatusLine().getStatusCode() >= 200) {
272             this.connMetrics.incrementResponseCount();
273             if (response.getEntity() != null) {
274                 this.response = response;
275                 prepareEncoder(response);
276             }
277         }
278 
279         this.session.setEvent(EventMask.WRITE);
280     }
281 
282     public boolean isResponseSubmitted() {
283         return this.response != null;
284     }
285 
286     public void consumeInput(final NHttpServiceHandler handler) {
287         consumeInput(new NHttpServerEventHandlerAdaptor(handler));
288     }
289 
290     public void produceOutput(NHttpServiceHandler handler) {
291         produceOutput(new NHttpServerEventHandlerAdaptor(handler));
292     }
293 
294 }