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