1 /* 2 * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0.1/httpcore/src/main/java/org/apache/http/protocol/HttpService.java $ 3 * $Revision: 744532 $ 4 * $Date: 2009-02-14 18:12:18 +0100 (Sat, 14 Feb 2009) $ 5 * 6 * ==================================================================== 7 * Licensed to the Apache Software Foundation (ASF) under one 8 * or more contributor license agreements. See the NOTICE file 9 * distributed with this work for additional information 10 * regarding copyright ownership. The ASF licenses this file 11 * to you under the Apache License, Version 2.0 (the 12 * "License"); you may not use this file except in compliance 13 * with the License. You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, 18 * software distributed under the License is distributed on an 19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 * KIND, either express or implied. See the License for the 21 * specific language governing permissions and limitations 22 * under the License. 23 * ==================================================================== 24 * 25 * This software consists of voluntary contributions made by many 26 * individuals on behalf of the Apache Software Foundation. For more 27 * information on the Apache Software Foundation, please see 28 * <http://www.apache.org/>. 29 * 30 */ 31 32 package org.apache.http.protocol; 33 34 import java.io.IOException; 35 36 import org.apache.http.ConnectionReuseStrategy; 37 import org.apache.http.HttpEntity; 38 import org.apache.http.HttpEntityEnclosingRequest; 39 import org.apache.http.HttpException; 40 import org.apache.http.HttpRequest; 41 import org.apache.http.HttpResponse; 42 import org.apache.http.HttpResponseFactory; 43 import org.apache.http.HttpServerConnection; 44 import org.apache.http.HttpStatus; 45 import org.apache.http.HttpVersion; 46 import org.apache.http.MethodNotSupportedException; 47 import org.apache.http.ProtocolException; 48 import org.apache.http.ProtocolVersion; 49 import org.apache.http.UnsupportedHttpVersionException; 50 import org.apache.http.entity.ByteArrayEntity; 51 import org.apache.http.params.HttpParams; 52 import org.apache.http.params.DefaultedHttpParams; 53 import org.apache.http.util.EncodingUtils; 54 55 /** 56 * HttpService is a server side HTTP protocol handler based in the blocking 57 * I/O model that implements the essential requirements of the HTTP protocol 58 * for the server side message processing as described by RFC 2616. 59 * <br> 60 * HttpService relies on {@link HttpProcessor} to generate mandatory protocol 61 * headers for all outgoing messages and apply common, cross-cutting message 62 * transformations to all incoming and outgoing messages, whereas individual 63 * {@link HttpRequestHandler}s are expected to take care of application specific 64 * content generation and processing. 65 * <br> 66 * HttpService relies on {@link HttpRequestHandler} to resolve matching request 67 * handler for a particular request URI of an incoming HTTP request. 68 * <br> 69 * HttpService can use optional {@link HttpExpectationVerifier} to ensure that 70 * incoming requests meet server's expectations. 71 * 72 * 73 * @version $Revision: 744532 $ 74 * 75 * @since 4.0 76 */ 77 public class HttpService { 78 79 private HttpParams params = null; 80 private HttpProcessor processor = null; 81 private HttpRequestHandlerResolver handlerResolver = null; 82 private ConnectionReuseStrategy connStrategy = null; 83 private HttpResponseFactory responseFactory = null; 84 private HttpExpectationVerifier expectationVerifier = null; 85 86 /** 87 * Create a new HTTP service. 88 * 89 * @param proc the processor to use on requests and responses 90 * @param connStrategy the connection reuse strategy 91 * @param responseFactory the response factory 92 */ 93 public HttpService( 94 final HttpProcessor proc, 95 final ConnectionReuseStrategy connStrategy, 96 final HttpResponseFactory responseFactory) { 97 super(); 98 setHttpProcessor(proc); 99 setConnReuseStrategy(connStrategy); 100 setResponseFactory(responseFactory); 101 } 102 103 public void setHttpProcessor(final HttpProcessor processor) { 104 if (processor == null) { 105 throw new IllegalArgumentException("HTTP processor may not be null"); 106 } 107 this.processor = processor; 108 } 109 110 public void setConnReuseStrategy(final ConnectionReuseStrategy connStrategy) { 111 if (connStrategy == null) { 112 throw new IllegalArgumentException("Connection reuse strategy may not be null"); 113 } 114 this.connStrategy = connStrategy; 115 } 116 117 public void setResponseFactory(final HttpResponseFactory responseFactory) { 118 if (responseFactory == null) { 119 throw new IllegalArgumentException("Response factory may not be null"); 120 } 121 this.responseFactory = responseFactory; 122 } 123 124 public void setHandlerResolver(final HttpRequestHandlerResolver handlerResolver) { 125 this.handlerResolver = handlerResolver; 126 } 127 128 public void setExpectationVerifier(final HttpExpectationVerifier expectationVerifier) { 129 this.expectationVerifier = expectationVerifier; 130 } 131 132 public HttpParams getParams() { 133 return this.params; 134 } 135 136 public void setParams(final HttpParams params) { 137 this.params = params; 138 } 139 140 /** 141 * Handles receives one HTTP request over the given connection within the 142 * given execution context and sends a response back to the client. 143 * 144 * @param conn the active connection to the client 145 * @param context the actual execution context. 146 * @throws IOException in case of an I/O error. 147 * @throws HttpException in case of HTTP protocol violation or a processing 148 * problem. 149 */ 150 public void handleRequest( 151 final HttpServerConnection conn, 152 final HttpContext context) throws IOException, HttpException { 153 154 context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); 155 156 HttpResponse response = null; 157 158 try { 159 160 HttpRequest request = conn.receiveRequestHeader(); 161 request.setParams( 162 new DefaultedHttpParams(request.getParams(), this.params)); 163 164 ProtocolVersion ver = 165 request.getRequestLine().getProtocolVersion(); 166 if (!ver.lessEquals(HttpVersion.HTTP_1_1)) { 167 // Downgrade protocol version if greater than HTTP/1.1 168 ver = HttpVersion.HTTP_1_1; 169 } 170 171 if (request instanceof HttpEntityEnclosingRequest) { 172 173 if (((HttpEntityEnclosingRequest) request).expectContinue()) { 174 response = this.responseFactory.newHttpResponse(ver, 175 HttpStatus.SC_CONTINUE, context); 176 response.setParams( 177 new DefaultedHttpParams(response.getParams(), this.params)); 178 179 if (this.expectationVerifier != null) { 180 try { 181 this.expectationVerifier.verify(request, response, context); 182 } catch (HttpException ex) { 183 response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0, 184 HttpStatus.SC_INTERNAL_SERVER_ERROR, context); 185 response.setParams( 186 new DefaultedHttpParams(response.getParams(), this.params)); 187 handleException(ex, response); 188 } 189 } 190 if (response.getStatusLine().getStatusCode() < 200) { 191 // Send 1xx response indicating the server expections 192 // have been met 193 conn.sendResponseHeader(response); 194 conn.flush(); 195 response = null; 196 conn.receiveRequestEntity((HttpEntityEnclosingRequest) request); 197 } 198 } else { 199 conn.receiveRequestEntity((HttpEntityEnclosingRequest) request); 200 } 201 } 202 203 if (response == null) { 204 response = this.responseFactory.newHttpResponse(ver, HttpStatus.SC_OK, context); 205 response.setParams( 206 new DefaultedHttpParams(response.getParams(), this.params)); 207 208 context.setAttribute(ExecutionContext.HTTP_REQUEST, request); 209 context.setAttribute(ExecutionContext.HTTP_RESPONSE, response); 210 211 this.processor.process(request, context); 212 doService(request, response, context); 213 } 214 215 // Make sure the request content is fully consumed 216 if (request instanceof HttpEntityEnclosingRequest) { 217 HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity(); 218 if (entity != null) { 219 entity.consumeContent(); 220 } 221 } 222 223 } catch (HttpException ex) { 224 response = this.responseFactory.newHttpResponse 225 (HttpVersion.HTTP_1_0, HttpStatus.SC_INTERNAL_SERVER_ERROR, 226 context); 227 response.setParams( 228 new DefaultedHttpParams(response.getParams(), this.params)); 229 handleException(ex, response); 230 } 231 232 this.processor.process(response, context); 233 conn.sendResponseHeader(response); 234 conn.sendResponseEntity(response); 235 conn.flush(); 236 237 if (!this.connStrategy.keepAlive(response, context)) { 238 conn.close(); 239 } 240 } 241 242 /** 243 * Handles the given exception and generates an HTTP response to be sent 244 * back to the client to inform about the exceptional condition encountered 245 * in the course of the request processing. 246 * 247 * @param ex the exception. 248 * @param response the HTTP response. 249 */ 250 protected void handleException(final HttpException ex, final HttpResponse response) { 251 if (ex instanceof MethodNotSupportedException) { 252 response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); 253 } else if (ex instanceof UnsupportedHttpVersionException) { 254 response.setStatusCode(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED); 255 } else if (ex instanceof ProtocolException) { 256 response.setStatusCode(HttpStatus.SC_BAD_REQUEST); 257 } else { 258 response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); 259 } 260 byte[] msg = EncodingUtils.getAsciiBytes(ex.getMessage()); 261 ByteArrayEntity entity = new ByteArrayEntity(msg); 262 entity.setContentType("text/plain; charset=US-ASCII"); 263 response.setEntity(entity); 264 } 265 266 /** 267 * The default implementation of this method attempts to resolve an 268 * {@link HttpRequestHandler} for the request URI of the given request 269 * and, if found, executes its 270 * {@link HttpRequestHandler#handle(HttpRequest, HttpResponse, HttpContext)} 271 * method. 272 * <p> 273 * Super-classes can override this method in order to provide a custom 274 * implementation of the request processing logic. 275 * 276 * @param request the HTTP request. 277 * @param response the HTTP response. 278 * @param context the execution context. 279 * @throws IOException in case of an I/O error. 280 * @throws HttpException in case of HTTP protocol violation or a processing 281 * problem. 282 */ 283 protected void doService( 284 final HttpRequest request, 285 final HttpResponse response, 286 final HttpContext context) throws HttpException, IOException { 287 HttpRequestHandler handler = null; 288 if (this.handlerResolver != null) { 289 String requestURI = request.getRequestLine().getUri(); 290 handler = this.handlerResolver.lookup(requestURI); 291 } 292 if (handler != null) { 293 handler.handle(request, response, context); 294 } else { 295 response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); 296 } 297 } 298 299 }