001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.protocol; 029 030import java.io.IOException; 031 032import org.apache.http.ConnectionReuseStrategy; 033import org.apache.http.HttpEntity; 034import org.apache.http.HttpEntityEnclosingRequest; 035import org.apache.http.HttpException; 036import org.apache.http.HttpRequest; 037import org.apache.http.HttpResponse; 038import org.apache.http.HttpResponseFactory; 039import org.apache.http.HttpServerConnection; 040import org.apache.http.HttpStatus; 041import org.apache.http.HttpVersion; 042import org.apache.http.MethodNotSupportedException; 043import org.apache.http.ProtocolException; 044import org.apache.http.UnsupportedHttpVersionException; 045import org.apache.http.annotation.Contract; 046import org.apache.http.annotation.ThreadingBehavior; 047import org.apache.http.entity.ByteArrayEntity; 048import org.apache.http.impl.DefaultConnectionReuseStrategy; 049import org.apache.http.impl.DefaultHttpResponseFactory; 050import org.apache.http.params.HttpParams; 051import org.apache.http.util.Args; 052import org.apache.http.util.EncodingUtils; 053import org.apache.http.util.EntityUtils; 054 055/** 056 * {@code HttpService} is a server side HTTP protocol handler based on 057 * the classic (blocking) I/O model. 058 * <p> 059 * {@code HttpService} relies on {@link HttpProcessor} to generate mandatory 060 * protocol headers for all outgoing messages and apply common, cross-cutting 061 * message transformations to all incoming and outgoing messages, whereas 062 * individual {@link HttpRequestHandler}s are expected to implement 063 * application specific content generation and processing. 064 * <p> 065 * {@code HttpService} uses {@link HttpRequestHandlerMapper} to map 066 * matching request handler for a particular request URI of an incoming HTTP 067 * request. 068 * <p> 069 * {@code HttpService} can use optional {@link HttpExpectationVerifier} 070 * to ensure that incoming requests meet server's expectations. 071 * 072 * @since 4.0 073 */ 074@SuppressWarnings("deprecation") 075@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) 076public class HttpService { 077 078 /** 079 * TODO: make all variables final in the next major version 080 */ 081 private volatile HttpParams params = null; 082 private volatile HttpProcessor processor = null; 083 private volatile HttpRequestHandlerMapper handlerMapper = null; 084 private volatile ConnectionReuseStrategy connStrategy = null; 085 private volatile HttpResponseFactory responseFactory = null; 086 private volatile HttpExpectationVerifier expectationVerifier = null; 087 088 /** 089 * Create a new HTTP service. 090 * 091 * @param processor the processor to use on requests and responses 092 * @param connStrategy the connection reuse strategy 093 * @param responseFactory the response factory 094 * @param handlerResolver the handler resolver. May be null. 095 * @param expectationVerifier the expectation verifier. May be null. 096 * @param params the HTTP parameters 097 * 098 * @since 4.1 099 * @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}