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.protocol;
29  
30  import java.io.IOException;
31  
32  import org.apache.http.ConnectionReuseStrategy;
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.HttpResponse;
38  import org.apache.http.HttpResponseFactory;
39  import org.apache.http.HttpServerConnection;
40  import org.apache.http.HttpStatus;
41  import org.apache.http.HttpVersion;
42  import org.apache.http.MethodNotSupportedException;
43  import org.apache.http.ProtocolException;
44  import org.apache.http.UnsupportedHttpVersionException;
45  import org.apache.http.annotation.Immutable;
46  import org.apache.http.entity.ByteArrayEntity;
47  import org.apache.http.impl.DefaultConnectionReuseStrategy;
48  import org.apache.http.impl.DefaultHttpResponseFactory;
49  import org.apache.http.params.HttpParams;
50  import org.apache.http.util.Args;
51  import org.apache.http.util.EncodingUtils;
52  import org.apache.http.util.EntityUtils;
53  
54  /**
55   * {@code HttpService} is a server side HTTP protocol handler based on
56   * the classic (blocking) I/O model.
57   * <p>
58   * {@code HttpService} relies on {@link HttpProcessor} to generate mandatory
59   * protocol headers for all outgoing messages and apply common, cross-cutting
60   * message transformations to all incoming and outgoing messages, whereas
61   * individual {@link HttpRequestHandler}s are expected to implement
62   * application specific content generation and processing.
63   * <p>
64   * {@code HttpService} uses {@link HttpRequestHandlerMapper} to map
65   * matching request handler for a particular request URI of an incoming HTTP
66   * request.
67   * <p>
68   * {@code HttpService} can use optional {@link HttpExpectationVerifier}
69   * to ensure that incoming requests meet server's expectations.
70   *
71   * @since 4.0
72   */
73  @SuppressWarnings("deprecation")
74  @Immutable // provided injected dependencies are immutable and deprecated methods are not used
75  public class HttpService {
76  
77      /**
78       * TODO: make all variables final in the next major version
79       */
80      private volatile HttpParams params = null;
81      private volatile HttpProcessor processor = null;
82      private volatile HttpRequestHandlerMapper handlerMapper = null;
83      private volatile ConnectionReuseStrategy connStrategy = null;
84      private volatile HttpResponseFactory responseFactory = null;
85      private volatile HttpExpectationVerifier expectationVerifier = null;
86  
87      /**
88       * Create a new HTTP service.
89       *
90       * @param processor            the processor to use on requests and responses
91       * @param connStrategy         the connection reuse strategy
92       * @param responseFactory      the response factory
93       * @param handlerResolver      the handler resolver. May be null.
94       * @param expectationVerifier  the expectation verifier. May be null.
95       * @param params               the HTTP parameters
96       *
97       * @since 4.1
98       * @deprecated (4.3) use {@link HttpService#HttpService(HttpProcessor, ConnectionReuseStrategy,
99       *   HttpResponseFactory, HttpRequestHandlerMapper, HttpExpectationVerifier)}
100      */
101     @Deprecated
102     public HttpService(
103             final HttpProcessor processor,
104             final ConnectionReuseStrategy connStrategy,
105             final HttpResponseFactory responseFactory,
106             final HttpRequestHandlerResolver handlerResolver,
107             final HttpExpectationVerifier expectationVerifier,
108             final HttpParams params) {
109         this(processor,
110              connStrategy,
111              responseFactory,
112              new HttpRequestHandlerResolverAdapter(handlerResolver),
113              expectationVerifier);
114         this.params = params;
115     }
116 
117     /**
118      * Create a new HTTP service.
119      *
120      * @param processor            the processor to use on requests and responses
121      * @param connStrategy         the connection reuse strategy
122      * @param responseFactory      the response factory
123      * @param handlerResolver      the handler resolver. May be null.
124      * @param params               the HTTP parameters
125      *
126      * @since 4.1
127      * @deprecated (4.3) use {@link HttpService#HttpService(HttpProcessor, ConnectionReuseStrategy,
128      *   HttpResponseFactory, HttpRequestHandlerMapper)}
129      */
130     @Deprecated
131     public HttpService(
132             final HttpProcessor processor,
133             final ConnectionReuseStrategy connStrategy,
134             final HttpResponseFactory responseFactory,
135             final HttpRequestHandlerResolver handlerResolver,
136             final HttpParams params) {
137         this(processor,
138              connStrategy,
139              responseFactory,
140              new HttpRequestHandlerResolverAdapter(handlerResolver),
141              null);
142         this.params = params;
143     }
144 
145     /**
146      * Create a new HTTP service.
147      *
148      * @param proc             the processor to use on requests and responses
149      * @param connStrategy     the connection reuse strategy
150      * @param responseFactory  the response factory
151      *
152      * @deprecated (4.1) use {@link HttpService#HttpService(HttpProcessor,
153      *  ConnectionReuseStrategy, HttpResponseFactory, HttpRequestHandlerResolver, HttpParams)}
154      */
155     @Deprecated
156     public HttpService(
157             final HttpProcessor proc,
158             final ConnectionReuseStrategy connStrategy,
159             final HttpResponseFactory responseFactory) {
160         super();
161         setHttpProcessor(proc);
162         setConnReuseStrategy(connStrategy);
163         setResponseFactory(responseFactory);
164     }
165 
166     /**
167      * Create a new HTTP service.
168      *
169      * @param processor the processor to use on requests and responses
170      * @param connStrategy the connection reuse strategy. If {@code null}
171      *   {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
172      * @param responseFactory  the response factory. If {@code null}
173      *   {@link DefaultHttpResponseFactory#INSTANCE} will be used.
174      * @param handlerMapper  the handler mapper. May be null.
175      * @param expectationVerifier the expectation verifier. May be null.
176      *
177      * @since 4.3
178      */
179     public HttpService(
180             final HttpProcessor processor,
181             final ConnectionReuseStrategy connStrategy,
182             final HttpResponseFactory responseFactory,
183             final HttpRequestHandlerMapper handlerMapper,
184             final HttpExpectationVerifier expectationVerifier) {
185         super();
186         this.processor =  Args.notNull(processor, "HTTP processor");
187         this.connStrategy = connStrategy != null ? connStrategy :
188             DefaultConnectionReuseStrategy.INSTANCE;
189         this.responseFactory = responseFactory != null ? responseFactory :
190             DefaultHttpResponseFactory.INSTANCE;
191         this.handlerMapper = handlerMapper;
192         this.expectationVerifier = expectationVerifier;
193     }
194 
195     /**
196      * Create a new HTTP service.
197      *
198      * @param processor the processor to use on requests and responses
199      * @param connStrategy the connection reuse strategy. If {@code null}
200      *   {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
201      * @param responseFactory  the response factory. If {@code null}
202      *   {@link DefaultHttpResponseFactory#INSTANCE} will be used.
203      * @param handlerMapper  the handler mapper. May be null.
204      *
205      * @since 4.3
206      */
207     public HttpService(
208             final HttpProcessor processor,
209             final ConnectionReuseStrategy connStrategy,
210             final HttpResponseFactory responseFactory,
211             final HttpRequestHandlerMapper handlerMapper) {
212         this(processor, connStrategy, responseFactory, handlerMapper, null);
213     }
214 
215     /**
216      * Create a new HTTP service.
217      *
218      * @param processor the processor to use on requests and responses
219      * @param handlerMapper  the handler mapper. May be null.
220      *
221      * @since 4.3
222      */
223     public HttpService(
224             final HttpProcessor processor, final HttpRequestHandlerMapper handlerMapper) {
225         this(processor, null, null, handlerMapper, null);
226     }
227 
228     /**
229      * @deprecated (4.1) set {@link HttpProcessor} using constructor
230      */
231     @Deprecated
232     public void setHttpProcessor(final HttpProcessor processor) {
233         Args.notNull(processor, "HTTP processor");
234         this.processor = processor;
235     }
236 
237     /**
238      * @deprecated (4.1) set {@link ConnectionReuseStrategy} using constructor
239      */
240     @Deprecated
241     public void setConnReuseStrategy(final ConnectionReuseStrategy connStrategy) {
242         Args.notNull(connStrategy, "Connection reuse strategy");
243         this.connStrategy = connStrategy;
244     }
245 
246     /**
247      * @deprecated (4.1) set {@link HttpResponseFactory} using constructor
248      */
249     @Deprecated
250     public void setResponseFactory(final HttpResponseFactory responseFactory) {
251         Args.notNull(responseFactory, "Response factory");
252         this.responseFactory = responseFactory;
253     }
254 
255     /**
256      * @deprecated (4.1) set {@link HttpResponseFactory} using constructor
257      */
258     @Deprecated
259     public void setParams(final HttpParams params) {
260         this.params = params;
261     }
262 
263     /**
264      * @deprecated (4.1) set {@link HttpRequestHandlerResolver} using constructor
265      */
266     @Deprecated
267     public void setHandlerResolver(final HttpRequestHandlerResolver handlerResolver) {
268         this.handlerMapper = new HttpRequestHandlerResolverAdapter(handlerResolver);
269     }
270 
271     /**
272      * @deprecated (4.1) set {@link HttpExpectationVerifier} using constructor
273      */
274     @Deprecated
275     public void setExpectationVerifier(final HttpExpectationVerifier expectationVerifier) {
276         this.expectationVerifier = expectationVerifier;
277     }
278 
279     /**
280      * @deprecated (4.3) no longer used.
281      */
282     @Deprecated
283     public HttpParams getParams() {
284         return this.params;
285     }
286 
287     /**
288      * Handles receives one HTTP request over the given connection within the
289      * given execution context and sends a response back to the client.
290      *
291      * @param conn the active connection to the client
292      * @param context the actual execution context.
293      * @throws IOException in case of an I/O error.
294      * @throws HttpException in case of HTTP protocol violation or a processing
295      *   problem.
296      */
297     public void handleRequest(
298             final HttpServerConnection conn,
299             final HttpContext context) throws IOException, HttpException {
300 
301         context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);
302 
303         HttpResponse response = null;
304 
305         try {
306 
307             final HttpRequest request = conn.receiveRequestHeader();
308             if (request instanceof HttpEntityEnclosingRequest) {
309 
310                 if (((HttpEntityEnclosingRequest) request).expectContinue()) {
311                     response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
312                             HttpStatus.SC_CONTINUE, context);
313                     if (this.expectationVerifier != null) {
314                         try {
315                             this.expectationVerifier.verify(request, response, context);
316                         } catch (final HttpException ex) {
317                             response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0,
318                                     HttpStatus.SC_INTERNAL_SERVER_ERROR, context);
319                             handleException(ex, response);
320                         }
321                     }
322                     if (response.getStatusLine().getStatusCode() < 200) {
323                         // Send 1xx response indicating the server expections
324                         // have been met
325                         conn.sendResponseHeader(response);
326                         conn.flush();
327                         response = null;
328                         conn.receiveRequestEntity((HttpEntityEnclosingRequest) request);
329                     }
330                 } else {
331                     conn.receiveRequestEntity((HttpEntityEnclosingRequest) request);
332                 }
333             }
334 
335             context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
336 
337             if (response == null) {
338                 response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
339                         HttpStatus.SC_OK, context);
340                 this.processor.process(request, context);
341                 doService(request, response, context);
342             }
343 
344             // Make sure the request content is fully consumed
345             if (request instanceof HttpEntityEnclosingRequest) {
346                 final HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
347                 EntityUtils.consume(entity);
348             }
349 
350         } catch (final HttpException ex) {
351             response = this.responseFactory.newHttpResponse
352                 (HttpVersion.HTTP_1_0, HttpStatus.SC_INTERNAL_SERVER_ERROR,
353                  context);
354             handleException(ex, response);
355         }
356 
357         context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
358 
359         this.processor.process(response, context);
360         conn.sendResponseHeader(response);
361         conn.sendResponseEntity(response);
362         conn.flush();
363 
364         if (!this.connStrategy.keepAlive(response, context)) {
365             conn.close();
366         }
367     }
368 
369     /**
370      * Handles the given exception and generates an HTTP response to be sent
371      * back to the client to inform about the exceptional condition encountered
372      * in the course of the request processing.
373      *
374      * @param ex the exception.
375      * @param response the HTTP response.
376      */
377     protected void handleException(final HttpException ex, final HttpResponse response) {
378         if (ex instanceof MethodNotSupportedException) {
379             response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
380         } else if (ex instanceof UnsupportedHttpVersionException) {
381             response.setStatusCode(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED);
382         } else if (ex instanceof ProtocolException) {
383             response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
384         } else {
385             response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
386         }
387         String message = ex.getMessage();
388         if (message == null) {
389             message = ex.toString();
390         }
391         final byte[] msg = EncodingUtils.getAsciiBytes(message);
392         final ByteArrayEntity entity = new ByteArrayEntity(msg);
393         entity.setContentType("text/plain; charset=US-ASCII");
394         response.setEntity(entity);
395     }
396 
397     /**
398      * The default implementation of this method attempts to resolve an
399      * {@link HttpRequestHandler} for the request URI of the given request
400      * and, if found, executes its
401      * {@link HttpRequestHandler#handle(HttpRequest, HttpResponse, HttpContext)}
402      * method.
403      * <p>
404      * Super-classes can override this method in order to provide a custom
405      * implementation of the request processing logic.
406      *
407      * @param request the HTTP request.
408      * @param response the HTTP response.
409      * @param context the execution context.
410      * @throws IOException in case of an I/O error.
411      * @throws HttpException in case of HTTP protocol violation or a processing
412      *   problem.
413      */
414     protected void doService(
415             final HttpRequest request,
416             final HttpResponse response,
417             final HttpContext context) throws HttpException, IOException {
418         HttpRequestHandler handler = null;
419         if (this.handlerMapper != null) {
420             handler = this.handlerMapper.lookup(request);
421         }
422         if (handler != null) {
423             handler.handle(request, response, context);
424         } else {
425             response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
426         }
427     }
428 
429     /**
430      * Adaptor class to transition from HttpRequestHandlerResolver to HttpRequestHandlerMapper.
431      */
432     @Deprecated
433     private static class HttpRequestHandlerResolverAdapter implements HttpRequestHandlerMapper {
434 
435         private final HttpRequestHandlerResolver resolver;
436 
437         public HttpRequestHandlerResolverAdapter(final HttpRequestHandlerResolver resolver) {
438             this.resolver = resolver;
439         }
440 
441         @Override
442         public HttpRequestHandler lookup(final HttpRequest request) {
443             return resolver.lookup(request.getRequestLine().getUri());
444         }
445 
446     }
447 
448 }