You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2017/08/07 19:13:22 UTC
[2/3] httpcomponents-core git commit: Improved handling of 1xx status
messages by the classic transport;
server expectation (expect-continue) handshake can now be implemented as a
cross-cutting aspect by both the classic and asynchronous transports
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
index ae6057f..3d799b3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
@@ -29,6 +29,7 @@ package org.apache.hc.core5.http.impl.io;
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
@@ -36,7 +37,7 @@ import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ConnectionReuseStrategy;
import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
@@ -50,10 +51,14 @@ import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.UnsupportedHttpVersionException;
import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
import org.apache.hc.core5.http.impl.Http1StreamListener;
-import org.apache.hc.core5.http.io.HttpExpectationVerifier;
import org.apache.hc.core5.http.io.HttpRequestHandler;
import org.apache.hc.core5.http.io.HttpServerConnection;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator;
+import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -72,9 +77,6 @@ import org.apache.hc.core5.util.Args;
* {@code HttpService} uses {@link HttpRequestMapper} to map
* matching request handler for a particular request URI of an incoming HTTP
* request.
- * <p>
- * {@code HttpService} can use optional {@link HttpExpectationVerifier}
- * to ensure that incoming requests meet server's expectations.
*
* @since 4.0
*/
@@ -82,74 +84,81 @@ import org.apache.hc.core5.util.Args;
public class HttpService {
private final HttpProcessor processor;
- private final HttpRequestMapper<HttpRequestHandler> handlerMapper;
+ private final HttpServerRequestHandler requestHandler;
private final ConnectionReuseStrategy connReuseStrategy;
- private final HttpResponseFactory<ClassicHttpResponse> responseFactory;
- private final HttpExpectationVerifier expectationVerifier;
private final Http1StreamListener streamListener;
/**
* Create a new HTTP service.
*
* @param processor the processor to use on requests and responses
- * @param connReuseStrategy the connection reuse strategy. If {@code null}
- * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
+ * @param handlerMapper the handler mapper
* @param responseFactory the response factory. If {@code null}
* {@link DefaultClassicHttpResponseFactory#INSTANCE} will be used.
- * @param handlerMapper the handler mapper. May be null.
- * @param expectationVerifier the expectation verifier. May be null.
- *
- * @since 4.3
+ * @param connReuseStrategy the connection reuse strategy. If {@code null}
+ * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
+ * @param streamListener message stream listener.
*/
public HttpService(
final HttpProcessor processor,
+ final HttpRequestMapper<HttpRequestHandler> handlerMapper,
final ConnectionReuseStrategy connReuseStrategy,
final HttpResponseFactory<ClassicHttpResponse> responseFactory,
- final HttpRequestMapper<HttpRequestHandler> handlerMapper,
- final HttpExpectationVerifier expectationVerifier,
final Http1StreamListener streamListener) {
- super();
- this.processor = Args.notNull(processor, "HTTP processor");
- this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy :
- DefaultConnectionReuseStrategy.INSTANCE;
- this.responseFactory = responseFactory != null ? responseFactory :
- DefaultClassicHttpResponseFactory.INSTANCE;
- this.handlerMapper = handlerMapper;
- this.expectationVerifier = expectationVerifier;
- this.streamListener = streamListener;
+ this(processor,
+ new BasicHttpServerExpectationDecorator(new BasicHttpServerRequestHandler(handlerMapper, responseFactory)),
+ connReuseStrategy,
+ streamListener);
}
/**
* Create a new HTTP service.
*
* @param processor the processor to use on requests and responses
+ * @param handlerMapper the handler mapper
* @param connReuseStrategy the connection reuse strategy. If {@code null}
* {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
* @param responseFactory the response factory. If {@code null}
* {@link DefaultClassicHttpResponseFactory#INSTANCE} will be used.
- * @param handlerMapper the handler mapper. May be null.
- *
- * @since 4.3
*/
public HttpService(
final HttpProcessor processor,
+ final HttpRequestMapper<HttpRequestHandler> handlerMapper,
final ConnectionReuseStrategy connReuseStrategy,
- final HttpResponseFactory<ClassicHttpResponse> responseFactory,
- final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
- this(processor, connReuseStrategy, responseFactory, handlerMapper, null, null);
+ final HttpResponseFactory<ClassicHttpResponse> responseFactory) {
+ this(processor, handlerMapper, connReuseStrategy, responseFactory, null);
}
/**
* Create a new HTTP service.
*
* @param processor the processor to use on requests and responses
- * @param handlerMapper the handler mapper. May be null.
+ * @param requestHandler the request handler.
+ * @param connReuseStrategy the connection reuse strategy. If {@code null}
+ * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
+ * @param streamListener message stream listener.
+ */
+ public HttpService(
+ final HttpProcessor processor,
+ final HttpServerRequestHandler requestHandler,
+ final ConnectionReuseStrategy connReuseStrategy,
+ final Http1StreamListener streamListener) {
+ super();
+ this.processor = Args.notNull(processor, "HTTP processor");
+ this.requestHandler = Args.notNull(requestHandler, "Request handler");
+ this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE;
+ this.streamListener = streamListener;
+ }
+
+ /**
+ * Create a new HTTP service.
*
- * @since 4.3
+ * @param processor the processor to use on requests and responses
+ * @param requestHandler the request handler.
*/
public HttpService(
- final HttpProcessor processor, final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
- this(processor, null, null, handlerMapper);
+ final HttpProcessor processor, final HttpServerRequestHandler requestHandler) {
+ this(processor, requestHandler, null, null);
}
/**
@@ -166,96 +175,95 @@ public class HttpService {
final HttpServerConnection conn,
final HttpContext context) throws IOException, HttpException {
- final ClassicHttpRequest request = conn.receiveRequestHeader();
- if (streamListener != null) {
- streamListener.onRequestHead(conn, request);
- }
- ClassicHttpResponse response = null;
+ final AtomicBoolean responseSubmitted = new AtomicBoolean(false);
try {
- try {
- conn.receiveRequestEntity(request);
- final ProtocolVersion transportVersion = request.getVersion();
- if (transportVersion != null) {
- context.setProtocolVersion(transportVersion);
- }
- context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession());
- context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails());
- context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
- this.processor.process(request, request.getEntity(), context);
+ final ClassicHttpRequest request = conn.receiveRequestHeader();
+ if (streamListener != null) {
+ streamListener.onRequestHead(conn, request);
+ }
+ conn.receiveRequestEntity(request);
+ final ProtocolVersion transportVersion = request.getVersion();
+ if (transportVersion != null) {
+ context.setProtocolVersion(transportVersion);
+ }
+ context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession());
+ context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails());
+ context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
+ this.processor.process(request, request.getEntity(), context);
- final Header expect = request.getFirstHeader(HttpHeaders.EXPECT);
- final boolean expectContinue = expect != null && "100-continue".equalsIgnoreCase(expect.getValue());
+ this.requestHandler.handle(request, new HttpServerRequestHandler.ResponseTrigger() {
- if (expectContinue) {
- final ClassicHttpResponse ack = this.responseFactory.newHttpResponse(HttpStatus.SC_CONTINUE);
- if (this.expectationVerifier != null) {
- this.expectationVerifier.verify(request, ack, context);
+ @Override
+ public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException {
+ if (responseSubmitted.get()) {
+ throw new HttpException("Response already submitted");
+ }
+ if (response.getCode() >= HttpStatus.SC_SUCCESS) {
+ throw new HttpException("Invalid intermediate response");
+ }
+ if (streamListener != null) {
+ streamListener.onResponseHead(conn, response);
}
- if (ack.getCode() < HttpStatus.SC_SUCCESS) {
- // Send 1xx response indicating the server expectations
- // have been met
- conn.sendResponseHeader(ack);
+ conn.sendResponseHeader(response);
+ conn.flush();
+ }
+
+ @Override
+ public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException {
+ try {
+ context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
+ processor.process(response, response.getEntity(), context);
+
+ responseSubmitted.set(true);
+ conn.sendResponseHeader(response);
if (streamListener != null) {
- streamListener.onResponseHead(conn, ack);
+ streamListener.onResponseHead(conn, response);
+ }
+ if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) {
+ conn.sendResponseEntity(response);
+ }
+ // Make sure the request content is fully consumed
+ final HttpEntity entity = request.getEntity();
+ if (entity != null && entity.isStreaming()) {
+ final InputStream instream = entity.getContent();
+ if (instream != null) {
+ instream.close();
+ }
+ }
+ final boolean keepAlive = connReuseStrategy.keepAlive(request, response, context);
+ if (streamListener != null) {
+ streamListener.onExchangeComplete(conn, keepAlive);
+ }
+ if (!keepAlive) {
+ conn.close();
}
conn.flush();
- } else {
- response = ack;
+ } finally {
+ response.close();
}
}
- if (response == null) {
- response = this.responseFactory.newHttpResponse(HttpStatus.SC_OK);
- doService(request, response, context);
- }
- } catch (final HttpException ex) {
- if (response != null) {
- response.close();
- }
- response = this.responseFactory.newHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR);
- handleException(ex, response);
- }
- context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
- this.processor.process(response, response.getEntity(), context);
- conn.sendResponseHeader(response);
- if (streamListener != null) {
- streamListener.onResponseHead(conn, response);
- }
- if (canResponseHaveBody(request, response)) {
- conn.sendResponseEntity(response);
- }
- conn.flush();
+ }, context);
- // Make sure the request content is fully consumed
- final HttpEntity entity = request.getEntity();
- if (entity != null && entity.isStreaming()) {
- final InputStream instream = entity.getContent();
- if (instream != null) {
- instream.close();
+ } catch (final HttpException ex) {
+ if (responseSubmitted.get()) {
+ throw ex;
+ } else {
+ try (final ClassicHttpResponse errorResponse = new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR)) {
+ handleException(ex, errorResponse);
+ errorResponse.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
+ context.setAttribute(HttpCoreContext.HTTP_RESPONSE, errorResponse);
+ this.processor.process(errorResponse, errorResponse.getEntity(), context);
+
+ conn.sendResponseHeader(errorResponse);
+ if (streamListener != null) {
+ streamListener.onResponseHead(conn, errorResponse);
+ }
+ conn.sendResponseEntity(errorResponse);
+ conn.close();
}
}
- final boolean keepAlive = this.connReuseStrategy.keepAlive(request, response, context);
- if (streamListener != null) {
- streamListener.onExchangeComplete(conn, keepAlive);
- }
- if (!keepAlive) {
- conn.close();
- }
- } finally {
- if (response != null) {
- response.close();
- }
- }
- }
-
- private boolean canResponseHaveBody(final ClassicHttpRequest request, final ClassicHttpResponse response) {
- if (request != null && "HEAD".equalsIgnoreCase(request.getMethod())) {
- return false;
}
- final int status = response.getCode();
- return status >= HttpStatus.SC_SUCCESS
- && status != HttpStatus.SC_NO_CONTENT
- && status != HttpStatus.SC_NOT_MODIFIED;
}
/**
@@ -291,36 +299,4 @@ public class HttpService {
return code;
}
- /**
- * The default implementation of this method attempts to resolve an
- * {@link HttpRequestHandler} for the request URI of the given request
- * and, if found, executes its
- * {@link HttpRequestHandler#handle(ClassicHttpRequest, ClassicHttpResponse, HttpContext)}
- * method.
- * <p>
- * Super-classes can override this method in order to provide a custom
- * implementation of the request processing logic.
- *
- * @param request the HTTP request.
- * @param response the HTTP response.
- * @param context the execution context.
- * @throws IOException in case of an I/O error.
- * @throws HttpException in case of HTTP protocol violation or a processing
- * problem.
- */
- protected void doService(
- final ClassicHttpRequest request,
- final ClassicHttpResponse response,
- final HttpContext context) throws HttpException, IOException {
- HttpRequestHandler handler = null;
- if (this.handlerMapper != null) {
- handler = this.handlerMapper.resolve(request, context);
- }
- if (handler != null) {
- handler.handle(request, response, context);
- } else {
- response.setCode(HttpStatus.SC_NOT_IMPLEMENTED);
- }
- }
-
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
index c5394a2..fc87b11 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
@@ -48,7 +48,6 @@ import org.apache.hc.core5.http.impl.LazyEntityDetails;
import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
import org.apache.hc.core5.http.nio.CapacityChannel;
import org.apache.hc.core5.http.nio.DataStreamChannel;
-import org.apache.hc.core5.http.nio.HttpContextAware;
import org.apache.hc.core5.http.nio.RequestChannel;
import org.apache.hc.core5.http.nio.ResourceHolder;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
@@ -150,10 +149,6 @@ class ClientHttp1StreamHandler implements ResourceHolder {
context.setProtocolVersion(transportVersion);
context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
- if (exchangeHandler instanceof HttpContextAware) {
- ((HttpContextAware) exchangeHandler).setContext(context);
- }
-
httpProcessor.process(request, entityDetails, context);
final boolean endStream = entityDetails == null;
@@ -214,7 +209,7 @@ class ClientHttp1StreamHandler implements ResourceHolder {
if (status < HttpStatus.SC_INFORMATIONAL) {
throw new ProtocolException("Invalid response: " + status);
}
- if (status < HttpStatus.SC_SUCCESS) {
+ if (status > HttpStatus.SC_CONTINUE && status < HttpStatus.SC_SUCCESS) {
exchangeHandler.consumeInformation(response);
} else {
if (!connectionReuseStrategy.keepAlive(committedRequest, response, context)) {
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
index 2dd95e7..9c8c6a3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
@@ -53,7 +53,6 @@ import org.apache.hc.core5.http.nio.BasicResponseProducer;
import org.apache.hc.core5.http.nio.CapacityChannel;
import org.apache.hc.core5.http.nio.DataStreamChannel;
import org.apache.hc.core5.http.nio.HandlerFactory;
-import org.apache.hc.core5.http.nio.HttpContextAware;
import org.apache.hc.core5.http.nio.ResourceHolder;
import org.apache.hc.core5.http.nio.ResponseChannel;
import org.apache.hc.core5.http.nio.support.ImmediateResponseExchangeHandler;
@@ -257,10 +256,6 @@ class ServerHttp1StreamHandler implements ResourceHolder {
context.setProtocolVersion(transportVersion);
context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
- if (exchangeHandler instanceof HttpContextAware) {
- ((HttpContextAware) exchangeHandler).setContext(context);
- }
-
final EntityDetails requestEntityDetails = requestEndStream ? null : new LazyEntityDetails(request);
final ResponseChannel responseChannel = new ResponseChannel() {
@@ -285,12 +280,12 @@ class ServerHttp1StreamHandler implements ResourceHolder {
};
try {
httpProcessor.process(request, requestEntityDetails, context);
- exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel);
+ exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context);
} catch (final HttpException ex) {
if (!responseCommitted.get()) {
final AsyncResponseProducer responseProducer = handleException(ex);
exchangeHandler = new ImmediateResponseExchangeHandler(responseProducer);
- exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel);
+ exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context);
} else {
throw ex;
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java
new file mode 100644
index 0000000..1db8c98
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java
@@ -0,0 +1,54 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.io;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+
+/**
+ * Handler that encapsulates the process of generating a response object
+ * from a {@link ClassicHttpResponse}.
+ *
+ *
+ * @since 4.0
+ */
+public interface HttpClientResponseHandler<T> {
+
+ /**
+ * Processes an {@link ClassicHttpResponse} and returns some value
+ * corresponding to that response.
+ *
+ * @param response The response to process
+ * @return A value determined by the response
+ *
+ * @throws IOException in case of a problem or the connection was aborted
+ */
+ T handleResponse(ClassicHttpResponse response) throws HttpException, IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java
deleted file mode 100644
index 7dc079a..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http.io;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.protocol.HttpContext;
-
-/**
- * Defines an interface to verify whether an incoming HTTP request meets
- * the target server's expectations.
- *
- * @since 4.0
- */
-public interface HttpExpectationVerifier {
-
- /**
- * Verifies whether the given request meets the server's expectations.
- * <p>
- * If the request fails to meet particular criteria, this method can
- * trigger a terminal response back to the client by setting the status
- * code of the response object to a value greater or equal to
- * {@code 200}. In this case the client will not have to transmit
- * the request body. If the request meets expectations this method can
- * terminate without modifying the response object. Per default the status
- * code of the response object will be set to {@code 100}.
- *
- * @param request the HTTP request.
- * @param response the HTTP response.
- * @param context the HTTP context.
- * @throws HttpException in case of an HTTP protocol violation.
- */
- void verify(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
- throws HttpException;
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java
new file mode 100644
index 0000000..76e5c97
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java
@@ -0,0 +1,44 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.io;
+
+import org.apache.hc.core5.http.HttpConnection;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * Information message callback.
+ *
+ * @since 5.0
+ */
+public interface HttpResponseInformationCallback {
+
+ void execute(HttpResponse response, HttpConnection connection, HttpContext context) throws HttpException;
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java
new file mode 100644
index 0000000..648b781
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java
@@ -0,0 +1,57 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.io;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.STATELESS)
+public interface HttpServerRequestHandler {
+
+ interface ResponseTrigger {
+
+ void sendInformation(ClassicHttpResponse response) throws HttpException, IOException;
+
+ void submitResponse(ClassicHttpResponse response) throws HttpException, IOException;
+
+ }
+
+ void handle(
+ ClassicHttpRequest request,
+ ResponseTrigger responseTrigger,
+ HttpContext context) throws HttpException, IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java
deleted file mode 100644
index 2782930..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.core5.http.io;
-
-import java.io.IOException;
-
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpException;
-
-/**
- * Handler that encapsulates the process of generating a response object
- * from a {@link ClassicHttpResponse}.
- *
- *
- * @since 4.0
- */
-public interface ResponseHandler<T> {
-
- /**
- * Processes an {@link ClassicHttpResponse} and returns some value
- * corresponding to that response.
- *
- * @param response The response to process
- * @return A value determined by the response
- *
- * @throws IOException in case of a problem or the connection was aborted
- */
- T handleResponse(ClassicHttpResponse response) throws HttpException, IOException;
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java
new file mode 100644
index 0000000..1920a4c
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java
@@ -0,0 +1,76 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.io.support;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public class BasicHttpServerExpectationDecorator implements HttpServerRequestHandler {
+
+ private final HttpServerRequestHandler requestHandler;
+
+ public BasicHttpServerExpectationDecorator(final HttpServerRequestHandler requestHandler) {
+ this.requestHandler = Args.notNull(requestHandler, "Request handler");
+ }
+
+ protected ClassicHttpResponse verify(final ClassicHttpRequest request, final HttpContext context) {
+ return null;
+ }
+
+ @Override
+ public final void handle(
+ final ClassicHttpRequest request,
+ final ResponseTrigger responseTrigger,
+ final HttpContext context) throws HttpException, IOException {
+ final Header expect = request.getFirstHeader(HttpHeaders.EXPECT);
+ if (expect != null && "100-continue".equalsIgnoreCase(expect.getValue())) {
+ final ClassicHttpResponse response = verify(request, context);
+ if (response == null) {
+ responseTrigger.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE));
+ } else {
+ responseTrigger.submitResponse(response);
+ return;
+ }
+ }
+ requestHandler.handle(request, responseTrigger, context);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java
new file mode 100644
index 0000000..1fa9a47
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java
@@ -0,0 +1,78 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.io.support;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpRequestMapper;
+import org.apache.hc.core5.http.HttpResponseFactory;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public class BasicHttpServerRequestHandler implements HttpServerRequestHandler {
+
+ private final HttpRequestMapper<HttpRequestHandler> handlerMapper;
+ private final HttpResponseFactory<ClassicHttpResponse> responseFactory;
+
+ public BasicHttpServerRequestHandler(
+ final HttpRequestMapper<HttpRequestHandler> handlerMapper,
+ final HttpResponseFactory<ClassicHttpResponse> responseFactory) {
+ this.handlerMapper = Args.notNull(handlerMapper, "Handler mapper");
+ this.responseFactory = responseFactory != null ? responseFactory : DefaultClassicHttpResponseFactory.INSTANCE;
+ }
+
+ public BasicHttpServerRequestHandler(final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
+ this(handlerMapper, null);
+ }
+
+ @Override
+ public void handle(
+ final ClassicHttpRequest request,
+ final ResponseTrigger responseTrigger,
+ final HttpContext context) throws HttpException, IOException {
+ final ClassicHttpResponse response = responseFactory.newHttpResponse(HttpStatus.SC_OK);
+ final HttpRequestHandler handler = handlerMapper != null ? handlerMapper.resolve(request, context) : null;
+ if (handler != null) {
+ handler.handle(request, response, context);
+ } else {
+ response.setCode(HttpStatus.SC_NOT_IMPLEMENTED);
+ }
+ responseTrigger.submitResponse(response);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
index 4f0758e..3c75820 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
@@ -31,6 +31,7 @@ import java.io.IOException;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.protocol.HttpContext;
/**
* Abstract asynchronous server side message exchange handler that acts as a request consumer
@@ -43,6 +44,7 @@ public interface AsyncServerExchangeHandler extends AsyncDataExchangeHandler {
void handleRequest(
HttpRequest request,
EntityDetails entityDetails,
- ResponseChannel responseChannel) throws HttpException, IOException;
+ ResponseChannel responseChannel,
+ HttpContext context) throws HttpException, IOException;
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
index a59471e..8ae9e29 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
@@ -32,6 +32,7 @@ import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.protocol.HttpContext;
/**
@@ -40,8 +41,18 @@ import org.apache.hc.core5.http.protocol.HttpContext;
@Contract(threading = ThreadingBehavior.STATELESS)
public interface AsyncServerRequestHandler<T> {
+ interface ResponseTrigger {
+
+ void sendInformation(HttpResponse response) throws HttpException, IOException;
+
+ void submitResponse(AsyncResponseProducer responseProducer) throws HttpException, IOException;
+
+ void pushPromise(HttpRequest promise, AsyncPushProducer responseProducer) throws HttpException, IOException;
+
+ }
+
AsyncRequestConsumer<T> prepare(HttpRequest request, HttpContext context) throws HttpException;
- void handle(T requestMessage, AsyncServerResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException;
+ void handle(T requestMessage, ResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException;
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java
deleted file mode 100644
index db2e42a..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.core5.http.nio;
-
-import java.io.IOException;
-
-import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.HttpResponse;
-
-/**
- * @since 5.0
- */
-@Contract(threading = ThreadingBehavior.SAFE)
-public interface AsyncServerResponseTrigger {
-
- void sendInformation(HttpResponse response) throws HttpException, IOException;
-
- void submitResponse(AsyncResponseProducer responseProducer) throws HttpException, IOException;
-
- void pushPromise(HttpRequest promise, AsyncPushProducer responseProducer) throws HttpException, IOException;
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java
deleted file mode 100644
index 3e920f1..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.core5.http.nio;
-
-import org.apache.hc.core5.http.protocol.HttpContext;
-
-/**
- * {@link HttpContext} aware protocol handler.
- *
- * @since 5.0
- */
-public interface HttpContextAware {
-
- void setContext(HttpContext context);
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
index 75a9101..233a9e6 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
@@ -47,7 +47,6 @@ import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.message.BasicHttpResponse;
import org.apache.hc.core5.http.message.HttpResponseWrapper;
-import org.apache.hc.core5.http.nio.HttpContextAware;
import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
import org.apache.hc.core5.http.nio.CapacityChannel;
import org.apache.hc.core5.http.nio.DataStreamChannel;
@@ -63,7 +62,7 @@ import org.apache.hc.core5.util.Asserts;
/**
* @since 5.0
*/
-public abstract class AbstractClassicServerExchangeHandler implements HttpContextAware, AsyncServerExchangeHandler {
+public abstract class AbstractClassicServerExchangeHandler implements AsyncServerExchangeHandler {
private enum State { IDLE, ACTIVE, COMPLETED }
@@ -72,7 +71,6 @@ public abstract class AbstractClassicServerExchangeHandler implements HttpContex
private final AtomicReference<State> state;
private final AtomicReference<Exception> exception;
- private volatile HttpContext context;
private volatile SharedInputBuffer inputBuffer;
private volatile SharedOutputBuffer outputBuffer;
@@ -87,11 +85,6 @@ public abstract class AbstractClassicServerExchangeHandler implements HttpContex
return exception.get();
}
- @Override
- public void setContext(final HttpContext context) {
- this.context = context;
- }
-
protected abstract void handle(
HttpRequest request, InputStream requestStream,
HttpResponse response, OutputStream responseStream,
@@ -101,14 +94,8 @@ public abstract class AbstractClassicServerExchangeHandler implements HttpContex
public final void handleRequest(
final HttpRequest request,
final EntityDetails entityDetails,
- final ResponseChannel responseChannel) throws HttpException, IOException {
-
- if (entityDetails != null) {
- final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
- if (h != null && "100-continue".equalsIgnoreCase(h.getValue())) {
- responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE));
- }
- }
+ final ResponseChannel responseChannel,
+ final HttpContext context) throws HttpException, IOException {
final AtomicBoolean responseCommitted = new AtomicBoolean(false);
final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK);
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
index 9a09237..88b13d4 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
@@ -35,20 +35,17 @@ import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.message.BasicHttpResponse;
import org.apache.hc.core5.http.nio.AsyncPushProducer;
import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
import org.apache.hc.core5.http.nio.AsyncResponseProducer;
import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
-import org.apache.hc.core5.http.nio.AsyncServerResponseTrigger;
+import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
import org.apache.hc.core5.http.nio.BasicResponseProducer;
import org.apache.hc.core5.http.nio.CapacityChannel;
import org.apache.hc.core5.http.nio.DataStreamChannel;
-import org.apache.hc.core5.http.nio.HttpContextAware;
import org.apache.hc.core5.http.nio.ResponseChannel;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Asserts;
@@ -56,66 +53,38 @@ import org.apache.hc.core5.util.Asserts;
/**
* @since 5.0
*/
-public abstract class AbstractServerExchangeHandler<T> implements HttpContextAware, AsyncServerExchangeHandler {
+public abstract class AbstractServerExchangeHandler<T> implements AsyncServerExchangeHandler {
private final AtomicReference<AsyncRequestConsumer<T>> requestConsumerRef;
private final AtomicReference<AsyncResponseProducer> responseProducerRef;
- private volatile HttpContext context;
- private volatile boolean expectationFailed;
-
public AbstractServerExchangeHandler() {
this.requestConsumerRef = new AtomicReference<>(null);
this.responseProducerRef = new AtomicReference<>(null);
}
- protected AsyncResponseProducer verify(
- final HttpRequest request,
- final HttpContext context) throws IOException, HttpException {
- return null;
- }
-
protected abstract AsyncRequestConsumer<T> supplyConsumer(
HttpRequest request,
HttpContext context) throws HttpException;
protected abstract void handle(
T requestMessage,
- AsyncServerResponseTrigger responseTrigger,
+ AsyncServerRequestHandler.ResponseTrigger responseTrigger,
HttpContext context) throws HttpException, IOException;
@Override
- public void setContext(final HttpContext context) {
- this.context = context;
- }
-
- @Override
public final void handleRequest(
final HttpRequest request,
final EntityDetails entityDetails,
- final ResponseChannel responseChannel) throws HttpException, IOException {
+ final ResponseChannel responseChannel,
+ final HttpContext context) throws HttpException, IOException {
final AsyncRequestConsumer<T> requestConsumer = supplyConsumer(request, context);
if (requestConsumer == null) {
throw new HttpException("Unable to handle request");
}
requestConsumerRef.set(requestConsumer);
-
- if (entityDetails != null) {
- final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
- if (h != null && "100-continue".equalsIgnoreCase(h.getValue())) {
- final AsyncResponseProducer producer = verify(request, context);
- if (producer != null) {
- expectationFailed = true;
- responseProducerRef.set(producer);
- producer.sendResponse(responseChannel);
- return;
- } else {
- responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE));
- }
- }
- }
- final AsyncServerResponseTrigger responseTrigger = new AsyncServerResponseTrigger() {
+ final AsyncServerRequestHandler.ResponseTrigger responseTrigger = new AsyncServerRequestHandler.ResponseTrigger() {
@Override
public void sendInformation(final HttpResponse response) throws HttpException, IOException {
@@ -176,33 +145,23 @@ public abstract class AbstractServerExchangeHandler<T> implements HttpContextAwa
@Override
public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
- if (!expectationFailed) {
- final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
- Asserts.notNull(requestConsumer, "Data consumer");
- requestConsumer.updateCapacity(capacityChannel);
- } else {
- capacityChannel.update(Integer.MAX_VALUE);
- }
+ final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
+ Asserts.notNull(requestConsumer, "Data consumer");
+ requestConsumer.updateCapacity(capacityChannel);
}
@Override
public final int consume(final ByteBuffer src) throws IOException {
- if (!expectationFailed) {
- final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
- Asserts.notNull(requestConsumer, "Data consumer");
- return requestConsumer.consume(src);
- } else {
- return Integer.MAX_VALUE;
- }
+ final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
+ Asserts.notNull(requestConsumer, "Data consumer");
+ return requestConsumer.consume(src);
}
@Override
public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
- if (!expectationFailed) {
- final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
- Asserts.notNull(requestConsumer, "Data consumer");
- requestConsumer.streamEnd(trailers);
- }
+ final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
+ Asserts.notNull(requestConsumer, "Data consumer");
+ requestConsumer.streamEnd(trailers);
}
@Override
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
new file mode 100644
index 0000000..59dcfd7
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
@@ -0,0 +1,160 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.nio.support;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.nio.AsyncResponseProducer;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.CapacityChannel;
+import org.apache.hc.core5.http.nio.DataStreamChannel;
+import org.apache.hc.core5.http.nio.ResponseChannel;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public class BasicAsyncServerExpectationDecorator implements AsyncServerExchangeHandler {
+
+ private final AsyncServerExchangeHandler handler;
+ private final AtomicReference<AsyncResponseProducer> responseProducerRef;
+
+ public BasicAsyncServerExpectationDecorator(final AsyncServerExchangeHandler handler) {
+ this.handler = Args.notNull(handler, "Handler");
+ this.responseProducerRef = new AtomicReference<>(null);
+ }
+
+ protected AsyncResponseProducer verify(
+ final HttpRequest request,
+ final HttpContext context) throws IOException, HttpException {
+ return null;
+ }
+
+ @Override
+ public final void handleRequest(
+ final HttpRequest request,
+ final EntityDetails entityDetails,
+ final ResponseChannel responseChannel,
+ final HttpContext context) throws HttpException, IOException {
+ if (entityDetails != null) {
+ final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
+ if (h != null && "100-continue".equalsIgnoreCase(h.getValue())) {
+ final AsyncResponseProducer producer = verify(request, context);
+ if (producer != null) {
+ responseProducerRef.set(producer);
+ producer.sendResponse(responseChannel);
+ return;
+ } else {
+ responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE));
+ }
+ }
+ }
+ handler.handleRequest(request, entityDetails, responseChannel, context);
+ }
+
+ @Override
+ public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
+ final AsyncResponseProducer responseProducer = responseProducerRef.get();
+ if (responseProducer == null) {
+ handler.updateCapacity(capacityChannel);
+ } else {
+ capacityChannel.update(Integer.MAX_VALUE);
+ }
+ }
+
+ @Override
+ public final int consume(final ByteBuffer src) throws IOException {
+ final AsyncResponseProducer responseProducer = responseProducerRef.get();
+ if (responseProducer == null) {
+ return handler.consume(src);
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
+ final AsyncResponseProducer responseProducer = responseProducerRef.get();
+ if (responseProducer == null) {
+ handler.streamEnd(trailers);
+ }
+ }
+
+ @Override
+ public final int available() {
+ final AsyncResponseProducer responseProducer = responseProducerRef.get();
+ if (responseProducer == null) {
+ return handler.available();
+ } else {
+ return responseProducer.available();
+ }
+ }
+
+ @Override
+ public final void produce(final DataStreamChannel channel) throws IOException {
+ final AsyncResponseProducer responseProducer = responseProducerRef.get();
+ if (responseProducer == null) {
+ handler.produce(channel);
+ } else {
+ responseProducer.produce(channel);
+ }
+ }
+
+ @Override
+ public final void failed(final Exception cause) {
+ try {
+ handler.failed(cause);
+ final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null);
+ if (dataProducer != null) {
+ dataProducer.failed(cause);
+ }
+ } finally {
+ releaseResources();
+ }
+ }
+
+ @Override
+ public final void releaseResources() {
+ handler.releaseResources();
+ final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null);
+ if (dataProducer != null) {
+ dataProducer.releaseResources();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
index e6ae855..555cc65 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
@@ -32,7 +32,6 @@ import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
-import org.apache.hc.core5.http.nio.AsyncServerResponseTrigger;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Args;
@@ -58,7 +57,7 @@ public class BasicServerExchangeHandler<T> extends AbstractServerExchangeHandler
@Override
protected void handle(
final T requestMessage,
- final AsyncServerResponseTrigger responseTrigger,
+ final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
final HttpContext context) throws HttpException, IOException {
requestHandler.handle(requestMessage, responseTrigger, context);
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
index 3e39cbd..28d0ee5 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
@@ -26,6 +26,7 @@
*/
package org.apache.hc.core5.http.nio.support;
+import org.apache.hc.core5.function.Decorator;
import org.apache.hc.core5.function.Supplier;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
@@ -43,13 +44,20 @@ import org.apache.hc.core5.util.Args;
public final class DefaultAsyncResponseExchangeHandlerFactory implements HandlerFactory<AsyncServerExchangeHandler> {
private final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper;
+ private final Decorator<AsyncServerExchangeHandler> decorator;
- public DefaultAsyncResponseExchangeHandlerFactory(final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper) {
+ public DefaultAsyncResponseExchangeHandlerFactory(
+ final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper,
+ final Decorator<AsyncServerExchangeHandler> decorator) {
this.mapper = Args.notNull(mapper, "Request handler mapper");
+ this.decorator = decorator;
}
- @Override
- public AsyncServerExchangeHandler create(final HttpRequest request, final HttpContext context) throws HttpException {
+ public DefaultAsyncResponseExchangeHandlerFactory(final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper) {
+ this(mapper, null);
+ }
+
+ private AsyncServerExchangeHandler createHandler(final HttpRequest request, final HttpContext context) throws HttpException {
try {
final Supplier<AsyncServerExchangeHandler> supplier = mapper.resolve(request, context);
if (supplier != null) {
@@ -61,4 +69,15 @@ public final class DefaultAsyncResponseExchangeHandlerFactory implements Handler
return new ImmediateResponseExchangeHandler(HttpStatus.SC_MISDIRECTED_REQUEST, "Not authoritative");
}
}
+
+ @Override
+ public AsyncServerExchangeHandler create(final HttpRequest request, final HttpContext context) throws HttpException {
+ final AsyncServerExchangeHandler handler = createHandler(request, context);
+ if (handler != null) {
+ return decorator != null ? decorator.decorate(handler) : handler;
+ } else {
+ return null;
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
index 81e94fb..66f2fe1 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
@@ -44,6 +44,7 @@ import org.apache.hc.core5.http.nio.CapacityChannel;
import org.apache.hc.core5.http.nio.DataStreamChannel;
import org.apache.hc.core5.http.nio.ResponseChannel;
import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
+import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Args;
/**
@@ -69,7 +70,8 @@ public final class ImmediateResponseExchangeHandler implements AsyncServerExchan
public void handleRequest(
final HttpRequest request,
final EntityDetails entityDetails,
- final ResponseChannel responseChannel) throws HttpException, IOException {
+ final ResponseChannel responseChannel,
+ final HttpContext context) throws HttpException, IOException {
responseProducer.sendResponse(responseChannel);
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
index 656bad5..d60ba6d 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
@@ -28,20 +28,23 @@
package org.apache.hc.core5.http.impl.io;
import java.io.IOException;
+import java.util.List;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHeaders;
-import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.io.HttpClientConnection;
+import org.apache.hc.core5.http.io.HttpResponseInformationCallback;
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http.protocol.HttpProcessor;
import org.junit.Assert;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
public class TestHttpRequestExecutor {
@@ -152,17 +155,31 @@ public class TestHttpRequestExecutor {
Mockito.verify(httprocessor).process(request, request.getEntity(), context);
Mockito.when(conn.receiveResponseHeader()).thenReturn(
- new BasicClassicHttpResponse(100, "OK"),
- new BasicClassicHttpResponse(101, "OK"),
- new BasicClassicHttpResponse(102, "OK"),
+ new BasicClassicHttpResponse(100, "Continue"),
+ new BasicClassicHttpResponse(110, "Huh?"),
+ new BasicClassicHttpResponse(111, "Huh?"),
new BasicClassicHttpResponse(200, "OK"));
- final ClassicHttpResponse response = executor.execute(request, conn, context);
+ final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class);
+
+ final ClassicHttpResponse response = executor.execute(request, conn, callback, context);
Mockito.verify(conn).sendRequestHeader(request);
Mockito.verify(conn).flush();
Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader();
Mockito.verify(conn, Mockito.times(1)).receiveResponseEntity(response);
+ final ArgumentCaptor<HttpResponse> responseCaptor = ArgumentCaptor.forClass(HttpResponse.class);
+ Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), Mockito.eq(conn), Mockito.eq(context));
+ final List<HttpResponse> infos = responseCaptor.getAllValues();
+ Assert.assertNotNull(infos);
+ Assert.assertEquals(2, infos.size());
+ final HttpResponse info1 = infos.get(0);
+ Assert.assertNotNull(info1);
+ Assert.assertEquals(110, info1.getCode());
+ final HttpResponse info2 = infos.get(1);
+ Assert.assertNotNull(info2);
+ Assert.assertEquals(111, info2.getCode());
+
executor.postProcess(response, httprocessor, context);
Mockito.verify(httprocessor).process(response, response.getEntity(), context);
@@ -313,7 +330,7 @@ public class TestHttpRequestExecutor {
}
@Test
- public void testExecutionEntityEnclosingRequestUnsupportedIntermediateResponse() throws Exception {
+ public void testExecutionEntityEnclosingRequestWithExpectContinueMultiple1xxResponses() throws Exception {
final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
final HttpRequestExecutor executor = new HttpRequestExecutor();
@@ -328,15 +345,36 @@ public class TestHttpRequestExecutor {
Mockito.verify(httprocessor).process(request, request.getEntity(), context);
Mockito.when(conn.receiveResponseHeader()).thenReturn(
- new BasicClassicHttpResponse(101, "OK"));
+ new BasicClassicHttpResponse(110, "Huh?"),
+ new BasicClassicHttpResponse(100, "Continue"),
+ new BasicClassicHttpResponse(111, "Huh?"),
+ new BasicClassicHttpResponse(200, "OK"));
Mockito.when(conn.isDataAvailable(Mockito.anyInt())).thenReturn(Boolean.TRUE);
- try {
- executor.execute(request, conn, context);
- Assert.fail("ProtocolException should have been thrown");
- } catch (final ProtocolException ex) {
- Mockito.verify(conn).close();
- }
+ final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class);
+
+ final ClassicHttpResponse response = executor.execute(request, conn, callback, context);
+ Mockito.verify(conn).sendRequestHeader(request);
+ Mockito.verify(conn).sendRequestEntity(request);
+ Mockito.verify(conn, Mockito.times(2)).flush();
+ Mockito.verify(conn, Mockito.times(2)).isDataAvailable(3000);
+ Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader();
+ Mockito.verify(conn).receiveResponseEntity(response);
+
+ final ArgumentCaptor<HttpResponse> responseCaptor = ArgumentCaptor.forClass(HttpResponse.class);
+ Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), Mockito.eq(conn), Mockito.eq(context));
+ final List<HttpResponse> infos = responseCaptor.getAllValues();
+ Assert.assertNotNull(infos);
+ Assert.assertEquals(2, infos.size());
+ final HttpResponse info1 = infos.get(0);
+ Assert.assertNotNull(info1);
+ Assert.assertEquals(110, info1.getCode());
+ final HttpResponse info2 = infos.get(1);
+ Assert.assertNotNull(info2);
+ Assert.assertEquals(111, info2.getCode());
+
+ executor.postProcess(response, httprocessor, context);
+ Mockito.verify(httprocessor).process(response, response.getEntity(), context);
}
@Test