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}