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 * <tt>HttpService</tt> is a server side HTTP protocol handler based on
56 * the classic (blocking) I/O model.
57 * <p/>
58 * <tt>HttpService</tt> 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 * <tt>HttpService</tt> uses {@link HttpRequestHandlerMapper} to map
65 * matching request handler for a particular request URI of an incoming HTTP
66 * request.
67 * <p/>
68 * <tt>HttpService</tt> 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</code>
171 * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
172 * @param responseFactory the response factory. If <code>null</code>
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</code>
200 * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
201 * @param responseFactory the response factory. If <code>null</code>
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(ExecutionContext.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(ExecutionContext.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(ExecutionContext.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 public HttpRequestHandler lookup(final HttpRequest request) {
442 return resolver.lookup(request.getRequestLine().getUri());
443 }
444
445 }
446
447 }