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 2016/10/31 17:33:33 UTC
svn commit: r1767339 [6/14] - in /httpcomponents/httpcore/trunk: ./
httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/
httpcore5-ab/src/test/java/org/apache/hc/core5/http/benchmark/
httpcore5-h2/src/main/java/org/apache/hc/core5/http2/boots...
Modified: httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpClient.java?rev=1767339&r1=1767338&r2=1767339&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpClient.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpClient.java Mon Oct 31 17:33:27 2016
@@ -26,133 +26,86 @@
*/
package org.apache.hc.core5.http.examples;
-import java.io.IOException;
-import java.io.InterruptedIOException;
+import java.net.URI;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.hc.core5.concurrent.FutureCallback;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.config.ConnectionConfig;
-import org.apache.hc.core5.http.impl.nio.BasicAsyncRequestProducer;
-import org.apache.hc.core5.http.impl.nio.BasicAsyncResponseConsumer;
-import org.apache.hc.core5.http.impl.nio.DefaultHttpClientIOEventHandlerFactory;
-import org.apache.hc.core5.http.impl.nio.HttpAsyncRequestExecutor;
-import org.apache.hc.core5.http.impl.nio.HttpAsyncRequester;
-import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
-import org.apache.hc.core5.http.pool.nio.BasicNIOConnPool;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.impl.nio.bootstrap.ClientEndpoint;
+import org.apache.hc.core5.http.impl.nio.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.impl.nio.bootstrap.RequesterBootstrap;
+import org.apache.hc.core5.http.nio.BasicRequestProducer;
+import org.apache.hc.core5.http.nio.BasicResponseConsumer;
+import org.apache.hc.core5.http.nio.command.ShutdownType;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
-import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
-import org.apache.hc.core5.http.protocol.RequestConnControl;
-import org.apache.hc.core5.http.protocol.RequestContent;
-import org.apache.hc.core5.http.protocol.RequestExpectContinue;
-import org.apache.hc.core5.http.protocol.RequestTargetHost;
-import org.apache.hc.core5.http.protocol.RequestUserAgent;
-import org.apache.hc.core5.reactor.ConnectingIOReactor;
-import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
-import org.apache.hc.core5.reactor.IOEventHandlerFactory;
-import org.apache.hc.core5.reactor.IOReactorConfig;
/**
- * Minimal asynchronous HTTP/1.1 client.
- * <p>
- * Please note that this example represents a minimal HTTP client implementation.
- * It does not support HTTPS as is.
- * You either need to provide BasicNIOConnPool with a connection factory
- * that supports SSL or use a more complex HttpAsyncClient.
+ * Asynchronous HTTP/1.1 request executor.
*/
public class NHttpClient {
public static void main(String[] args) throws Exception {
- // Create HTTP protocol processing chain
- HttpProcessor httpproc = HttpProcessorBuilder.create()
- // Use standard client-side protocol interceptors
- .add(new RequestContent())
- .add(new RequestTargetHost())
- .add(new RequestConnControl())
- .add(new RequestUserAgent("Test/1.1"))
- .add(new RequestExpectContinue()).build();
- // Create client-side HTTP protocol handler
- HttpAsyncRequestExecutor protocolHandler = new HttpAsyncRequestExecutor();
- // Create client-side I/O event handler factory
- final IOEventHandlerFactory eventHandlerFactory = new DefaultHttpClientIOEventHandlerFactory(
- protocolHandler,
- ConnectionConfig.DEFAULT);
- // Create client-side I/O reactor
- final ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(
- eventHandlerFactory,
- IOReactorConfig.DEFAULT);
- // Create HTTP connection pool
- BasicNIOConnPool pool = new BasicNIOConnPool(ioReactor);
- // Limit total number of connections to just two
- pool.setDefaultMaxPerRoute(2);
- pool.setMaxTotal(2);
- // Run the I/O reactor in a separate thread
- Thread t = new Thread(new Runnable() {
+ // Create and start requester
+ final HttpAsyncRequester requester = RequesterBootstrap.bootstrap().create();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
- try {
- // Ready to go!
- ioReactor.execute();
- } catch (InterruptedIOException ex) {
- System.err.println("Interrupted");
- } catch (IOException e) {
- System.err.println("I/O error: " + e.getMessage());
- }
- System.out.println("Shutdown");
+ System.out.println("HTTP requester shutting down");
+ requester.shutdown(3, TimeUnit.SECONDS);
}
-
});
- // Start the client thread
- t.start();
- // Create HTTP requester
- HttpAsyncRequester requester = new HttpAsyncRequester(httpproc);
+ requester.start();
+
// Execute HTTP GETs to the following hosts and
HttpHost[] targets = new HttpHost[] {
new HttpHost("www.apache.org", 80, "http"),
- new HttpHost("www.verisign.com", 443, "https"),
- new HttpHost("www.google.com", 80, "http")
+ new HttpHost("hc.apache.org", 80, "http")
};
+
final CountDownLatch latch = new CountDownLatch(targets.length);
for (final HttpHost target: targets) {
- ClassicHttpRequest request = new BasicClassicHttpRequest("GET", "/");
+ final Future<ClientEndpoint> future = requester.connect(target, 5, TimeUnit.SECONDS);
+ final ClientEndpoint clientEndpoint = future.get();
HttpCoreContext coreContext = HttpCoreContext.create();
- requester.execute(
- new BasicAsyncRequestProducer(target, request),
- new BasicAsyncResponseConsumer(),
- pool,
- coreContext,
- // Handle HTTP response from a callback
- new FutureCallback<ClassicHttpResponse>() {
-
- @Override
- public void completed(final ClassicHttpResponse response) {
- latch.countDown();
- System.out.println(target + "->" + response.getCode());
- }
-
- @Override
- public void failed(final Exception ex) {
- latch.countDown();
- System.out.println(target + "->" + ex);
- }
-
- @Override
- public void cancelled() {
- latch.countDown();
- System.out.println(target + " cancelled");
- }
+ clientEndpoint.execute(
+ new BasicRequestProducer("GET", URI.create("/")),
+ new BasicResponseConsumer<>(new StringAsyncEntityConsumer()),
+ coreContext, new FutureCallback<Message<HttpResponse, String>>() {
+
+ @Override
+ public void completed(final Message<HttpResponse, String> message) {
+ latch.countDown();
+ clientEndpoint.shutdown(ShutdownType.IMMEDIATE);
+ HttpResponse response = message.getHead();
+ System.out.println(target + "->" + response.getCode());
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ latch.countDown();
+ clientEndpoint.shutdown(ShutdownType.IMMEDIATE);
+ System.out.println(target + "->" + ex);
+ }
+
+ @Override
+ public void cancelled() {
+ latch.countDown();
+ clientEndpoint.shutdown(ShutdownType.IMMEDIATE);
+ System.out.println(target + " cancelled");
+ }
- });
+ });
}
+
latch.await();
System.out.println("Shutting down I/O reactor");
- ioReactor.shutdown(3, TimeUnit.SECONDS);
- System.out.println("Done");
+ requester.initiateShutdown();
}
}
Modified: httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpFileServer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpFileServer.java?rev=1767339&r1=1767338&r2=1767339&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpFileServer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpFileServer.java Mon Oct 31 17:33:27 2016
@@ -28,38 +28,40 @@ package org.apache.hc.core5.http.example
import java.io.File;
import java.io.IOException;
-import java.net.URL;
-import java.net.URLDecoder;
+import java.net.InetSocketAddress;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
-import javax.net.ssl.SSLContext;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ConnectionClosedException;
import org.apache.hc.core5.http.ExceptionListener;
import org.apache.hc.core5.http.HttpConnection;
import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.MethodNotSupportedException;
-import org.apache.hc.core5.http.bootstrap.nio.HttpServer;
-import org.apache.hc.core5.http.bootstrap.nio.ServerBootstrap;
-import org.apache.hc.core5.http.entity.ContentType;
-import org.apache.hc.core5.http.impl.nio.BasicAsyncRequestConsumer;
-import org.apache.hc.core5.http.impl.nio.BasicAsyncResponseProducer;
-import org.apache.hc.core5.http.nio.HttpAsyncExchange;
-import org.apache.hc.core5.http.nio.HttpAsyncRequestConsumer;
-import org.apache.hc.core5.http.nio.HttpAsyncRequestHandler;
-import org.apache.hc.core5.http.nio.entity.NFileEntity;
-import org.apache.hc.core5.http.nio.entity.NStringEntity;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.impl.nio.ConnectionListener;
+import org.apache.hc.core5.http.impl.nio.bootstrap.HttpAsyncServer;
+import org.apache.hc.core5.http.impl.nio.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.io.entity.ContentType;
+import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
+import org.apache.hc.core5.http.nio.BasicRequestConsumer;
+import org.apache.hc.core5.http.nio.BasicResponseProducer;
+import org.apache.hc.core5.http.nio.entity.FileEntityProducer;
+import org.apache.hc.core5.http.nio.entity.NoopEntityConsumer;
+import org.apache.hc.core5.http.nio.support.RequestConsumerSupplier;
+import org.apache.hc.core5.http.nio.support.ResponseHandler;
+import org.apache.hc.core5.http.nio.support.ResponseTrigger;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.reactor.IOReactorConfig;
-import org.apache.hc.core5.ssl.SSLContexts;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
/**
- * Embedded HTTP/1.1 file server based on a non-blocking I/O model and capable of direct channel
- * (zero copy) data transfer.
+ * Asynchronous embedded HTTP/1.1 file server.
*/
public class NHttpFileServer {
@@ -69,120 +71,133 @@ public class NHttpFileServer {
System.exit(1);
}
// Document root directory
- File docRoot = new File(args[0]);
+ final File docRoot = new File(args[0]);
int port = 8080;
if (args.length >= 2) {
port = Integer.parseInt(args[1]);
}
- SSLContext sslcontext = null;
- if (port == 8443) {
- // Initialize SSL context
- URL url = NHttpFileServer.class.getResource("/my.keystore");
- if (url == null) {
- System.out.println("Keystore not found");
- System.exit(1);
- }
- sslcontext = SSLContexts.custom()
- .loadKeyMaterial(url, "secret".toCharArray(), "secret".toCharArray())
- .build();
- }
-
IOReactorConfig config = IOReactorConfig.custom()
.setSoTimeout(15000)
.setTcpNoDelay(true)
.build();
- final HttpServer server = ServerBootstrap.bootstrap()
- .setListenerPort(port)
- .setServerInfo("Test/1.1")
+ final HttpAsyncServer server = ServerBootstrap.bootstrap()
.setIOReactorConfig(config)
- .setSslContext(sslcontext)
- .setExceptionListener(ExceptionListener.STD_ERR)
- .registerHandler("*", new HttpFileHandler(docRoot))
- .create();
+ .setExceptionListener(new ExceptionListener() {
- server.start();
- server.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
+ @Override
+ public void onError(final Exception ex) {
+ if (ex instanceof ConnectionClosedException) {
+ return;
+ }
+ if (ex instanceof SocketTimeoutException) {
+ System.out.println("Timeout");
+ } else if (ex instanceof IOException) {
+ System.out.println("I/O error: " + ex.getMessage());
+ } else {
+ ex.printStackTrace();
+ }
+ }
+ })
+ .setConnectionListener(new ConnectionListener() {
+
+ @Override
+ public void onConnect(final HttpConnection connection) {
+ System.out.println(connection + " connected");
+ }
+
+ @Override
+ public void onError(final HttpConnection connection, final Exception ex) {
+ System.err.println(connection + " error: " + ex.getMessage());
+ }
+
+ @Override
+ public void onDisconnect(final HttpConnection connection) {
+ System.out.println(connection + " disconnected");
+ }
+
+ })
+ .register("*", new RequestConsumerSupplier<Message<HttpRequest, Void>>() {
+
+ @Override
+ public AsyncRequestConsumer<Message<HttpRequest, Void>> get(
+ final HttpRequest request,
+ final HttpContext context) throws HttpException {
+ return new BasicRequestConsumer<>(new NoopEntityConsumer());
+ }
+
+ }, new ResponseHandler<Message<HttpRequest, Void>>() {
+
+ @Override
+ public void handle(
+ final Message<HttpRequest, Void> message,
+ final ResponseTrigger responseTrigger,
+ final HttpContext context) throws HttpException, IOException {
+ HttpRequest request = message.getHead();
+ URI requestUri;
+ try {
+ requestUri = request.getUri();
+ } catch (URISyntaxException ex) {
+ throw new ProtocolException(ex.getMessage(), ex);
+ }
+ String path = requestUri.getPath();
+ final File file = new File(docRoot, path);
+ if (!file.exists()) {
+
+ System.out.println("File " + file.getPath() + " not found");
+ responseTrigger.submitResponse(new BasicResponseProducer(
+ HttpStatus.SC_NOT_FOUND,
+ "<html><body><h1>File" + file.getPath() +
+ " not found</h1></body></html>",
+ ContentType.TEXT_HTML));
+
+ } else if (!file.canRead() || file.isDirectory()) {
+
+ System.out.println("Cannot read file " + file.getPath());
+ responseTrigger.submitResponse(new BasicResponseProducer(
+ HttpStatus.SC_FORBIDDEN,
+ "<html><body><h1>Access denied</h1></body></html>",
+ ContentType.TEXT_HTML));
+
+ } else {
+
+ final ContentType contentType;
+ final String filename = file.getName().toLowerCase(Locale.ROOT);
+ if (filename.endsWith(".txt")) {
+ contentType = ContentType.TEXT_PLAIN;
+ } else if (filename.endsWith(".html") || filename.endsWith(".htm")) {
+ contentType = ContentType.TEXT_HTML;
+ } else if (filename.endsWith(".xml")) {
+ contentType = ContentType.TEXT_XML;
+ } else {
+ contentType = ContentType.DEFAULT_BINARY;
+ }
+
+ final HttpConnection connection = (HttpConnection) context.getAttribute(HttpCoreContext.HTTP_CONNECTION);
+
+ System.out.println(connection + " serving file " + file.getPath());
+ responseTrigger.submitResponse(new BasicResponseProducer(
+ HttpStatus.SC_OK, new FileEntityProducer(file, contentType)));
+ }
+ }
+
+ })
+ .create();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
+ System.out.println("HTTP server shutting down");
server.shutdown(5, TimeUnit.SECONDS);
}
});
- }
-
- static class HttpFileHandler implements HttpAsyncRequestHandler<ClassicHttpRequest> {
-
- private final File docRoot;
-
- public HttpFileHandler(final File docRoot) {
- super();
- this.docRoot = docRoot;
- }
-
- @Override
- public HttpAsyncRequestConsumer<ClassicHttpRequest> processRequest(
- final ClassicHttpRequest request,
- final HttpContext context) {
- // Buffer request content in memory for simplicity
- return new BasicAsyncRequestConsumer();
- }
-
- @Override
- public void handle(
- final ClassicHttpRequest request,
- final HttpAsyncExchange httpexchange,
- final HttpContext context) throws HttpException, IOException {
- ClassicHttpResponse response = httpexchange.getResponse();
- handleInternal(request, response, context);
- httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
- }
-
- private void handleInternal(
- final ClassicHttpRequest request,
- final ClassicHttpResponse response,
- final HttpContext context) throws HttpException, IOException {
-
- String method = request.getMethod().toUpperCase(Locale.ENGLISH);
- if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
- throw new MethodNotSupportedException(method + " method not supported");
- }
-
- String path = request.getPath();
- final File file = new File(this.docRoot, URLDecoder.decode(path, "UTF-8"));
- if (!file.exists()) {
-
- response.setCode(HttpStatus.SC_NOT_FOUND);
- NStringEntity entity = new NStringEntity(
- "<html><body><h1>File" + file.getPath() +
- " not found</h1></body></html>",
- ContentType.create("text/html", "UTF-8"));
- response.setEntity(entity);
- System.out.println("File " + file.getPath() + " not found");
-
- } else if (!file.canRead() || file.isDirectory()) {
-
- response.setCode(HttpStatus.SC_FORBIDDEN);
- NStringEntity entity = new NStringEntity(
- "<html><body><h1>Access denied</h1></body></html>",
- ContentType.create("text/html", "UTF-8"));
- response.setEntity(entity);
- System.out.println("Cannot read file " + file.getPath());
-
- } else {
-
- HttpCoreContext coreContext = HttpCoreContext.adapt(context);
- HttpConnection conn = coreContext.getConnection(HttpConnection.class);
- response.setCode(HttpStatus.SC_OK);
- NFileEntity body = new NFileEntity(file, ContentType.create("text/html"));
- response.setEntity(body);
- System.out.println(conn + ": serving file " + file.getPath());
- }
- }
-
+ server.start();
+ ListenerEndpoint listenerEndpoint = server.listen(new InetSocketAddress(port));
+ listenerEndpoint.waitFor();
+ System.out.print("Listening on " + listenerEndpoint.getAddress());
+ server.awaitShutdown(Long.MAX_VALUE, TimeUnit.DAYS);
}
}
Modified: httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpReverseProxy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpReverseProxy.java?rev=1767339&r1=1767338&r2=1767339&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpReverseProxy.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/NHttpReverseProxy.java Mon Oct 31 17:33:27 2016
@@ -29,834 +29,657 @@ package org.apache.hc.core5.http.example
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
-import java.net.URI;
import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
-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.concurrent.FutureCallback;
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HeaderElements;
+import org.apache.hc.core5.http.HttpConnection;
import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
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.config.ConnectionConfig;
-import org.apache.hc.core5.http.entity.ContentType;
-import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
-import org.apache.hc.core5.http.impl.nio.BasicAsyncResponseProducer;
-import org.apache.hc.core5.http.impl.nio.DefaultHttpClientIOEventHandlerFactory;
-import org.apache.hc.core5.http.impl.nio.DefaultHttpServerIOEventHandlerFactory;
-import org.apache.hc.core5.http.impl.nio.HttpAsyncRequestExecutor;
-import org.apache.hc.core5.http.impl.nio.HttpAsyncRequester;
-import org.apache.hc.core5.http.impl.nio.HttpAsyncService;
-import org.apache.hc.core5.http.impl.nio.UriHttpAsyncRequestHandlerMapper;
-import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
-import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-import org.apache.hc.core5.http.nio.ContentDecoder;
-import org.apache.hc.core5.http.nio.ContentEncoder;
-import org.apache.hc.core5.http.nio.HttpAsyncExchange;
-import org.apache.hc.core5.http.nio.HttpAsyncRequestConsumer;
-import org.apache.hc.core5.http.nio.HttpAsyncRequestHandler;
-import org.apache.hc.core5.http.nio.HttpAsyncRequestHandlerMapper;
-import org.apache.hc.core5.http.nio.HttpAsyncRequestProducer;
-import org.apache.hc.core5.http.nio.HttpAsyncResponseConsumer;
-import org.apache.hc.core5.http.nio.HttpAsyncResponseProducer;
-import org.apache.hc.core5.http.nio.IOControl;
-import org.apache.hc.core5.http.nio.NHttpClientConnection;
-import org.apache.hc.core5.http.nio.NHttpConnection;
-import org.apache.hc.core5.http.nio.NHttpServerConnection;
-import org.apache.hc.core5.http.nio.entity.NStringEntity;
-import org.apache.hc.core5.http.pool.nio.BasicNIOConnPool;
-import org.apache.hc.core5.http.pool.nio.BasicNIOPoolEntry;
+import org.apache.hc.core5.http.impl.BasicEntityDetails;
+import org.apache.hc.core5.http.impl.LazyEntityDetails;
+import org.apache.hc.core5.http.impl.nio.ConnectionListener;
+import org.apache.hc.core5.http.impl.nio.ExpandableBuffer;
+import org.apache.hc.core5.http.impl.nio.Http1StreamListener;
+import org.apache.hc.core5.http.impl.nio.bootstrap.ClientEndpoint;
+import org.apache.hc.core5.http.impl.nio.bootstrap.ClientEndpointImpl;
+import org.apache.hc.core5.http.impl.nio.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.impl.nio.bootstrap.HttpAsyncServer;
+import org.apache.hc.core5.http.impl.nio.bootstrap.RequesterBootstrap;
+import org.apache.hc.core5.http.impl.nio.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.impl.nio.pool.BasicNIOConnPool;
+import org.apache.hc.core5.http.impl.nio.pool.BasicNIOPoolEntry;
+import org.apache.hc.core5.http.io.entity.ContentType;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
+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.ExpectationChannel;
+import org.apache.hc.core5.http.nio.RequestChannel;
+import org.apache.hc.core5.http.nio.ResponseChannel;
+import org.apache.hc.core5.http.nio.Supplier;
import org.apache.hc.core5.http.protocol.HttpContext;
-import org.apache.hc.core5.http.protocol.HttpCoreContext;
-import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
-import org.apache.hc.core5.http.protocol.RequestConnControl;
-import org.apache.hc.core5.http.protocol.RequestContent;
-import org.apache.hc.core5.http.protocol.RequestExpectContinue;
-import org.apache.hc.core5.http.protocol.RequestTargetHost;
-import org.apache.hc.core5.http.protocol.RequestUserAgent;
-import org.apache.hc.core5.http.protocol.ResponseConnControl;
-import org.apache.hc.core5.http.protocol.ResponseContent;
-import org.apache.hc.core5.http.protocol.ResponseDate;
-import org.apache.hc.core5.http.protocol.ResponseServer;
import org.apache.hc.core5.pool.PoolStats;
-import org.apache.hc.core5.reactor.ConnectingIOReactor;
-import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
-import org.apache.hc.core5.reactor.DefaultListeningIOReactor;
+import org.apache.hc.core5.reactor.ConnectionInitiator;
import org.apache.hc.core5.reactor.IOReactorConfig;
-import org.apache.hc.core5.reactor.ListeningIOReactor;
+import org.apache.hc.core5.reactor.IOSession;
+import org.apache.hc.core5.util.HeapByteBufferAllocator;
/**
- * Asynchronous, fully streaming HTTP/1.1 reverse proxy.
+ * Asynchronous embedded HTTP/1.1 reverse proxy with full content streaming.
*/
public class NHttpReverseProxy {
public static void main(String[] args) throws Exception {
if (args.length < 1) {
- System.out.println("Usage: NHttpReverseProxy <hostname> [port]");
+ System.out.println("Usage: NHttpReverseProxy <hostname> [listener port]");
System.exit(1);
}
- URI uri = new URI(args[0]);
+ // Target host
+ final HttpHost targetHost = HttpHost.create(args[0]);
int port = 8080;
if (args.length > 1) {
port = Integer.parseInt(args[1]);
}
- // Target host
- HttpHost targetHost = new HttpHost(
- uri.getHost(),
- uri.getPort() > 0 ? uri.getPort() : 80,
- uri.getScheme() != null ? uri.getScheme() : "http");
-
System.out.println("Reverse proxy to " + targetHost);
IOReactorConfig config = IOReactorConfig.custom()
- .setIoThreadCount(1)
.setSoTimeout(3000)
.setConnectTimeout(3000)
.build();
- // Set up HTTP protocol processor for outgoing connections
- HttpProcessor outhttpproc;
- outhttpproc = new DefaultHttpProcessor(
- new RequestContent(),
- new RequestTargetHost(),
- new RequestConnControl(),
- new RequestUserAgent("Test/1.1"),
- new RequestExpectContinue());
-
- ProxyClientProtocolHandler clientProtocolHandler = new ProxyClientProtocolHandler();
- final ConnectingIOReactor connectingIOReactor = new DefaultConnectingIOReactor(
- new DefaultHttpClientIOEventHandlerFactory(clientProtocolHandler, ConnectionConfig.DEFAULT));
- HttpAsyncRequester executor = new HttpAsyncRequester(outhttpproc);
-
- ProxyConnPool connPool = new ProxyConnPool(connectingIOReactor, 0);
- connPool.setMaxTotal(100);
- connPool.setDefaultMaxPerRoute(20);
-
+ final HttpAsyncRequester requester = RequesterBootstrap.bootstrap()
+ .setIOReactorConfig(config)
+ .setConnectionListener(new ConnectionListener() {
+
+ @Override
+ public void onConnect(final HttpConnection connection) {
+ System.out.println("[proxy->origin] connection open " + connection);
+ }
- // Set up HTTP protocol processor for incoming connections
- HttpProcessor inhttpproc = new DefaultHttpProcessor(
- new ResponseDate(),
- new ResponseServer("Test/1.1"),
- new ResponseContent(),
- new ResponseConnControl());
-
- UriHttpAsyncRequestHandlerMapper handlerRegistry = new UriHttpAsyncRequestHandlerMapper();
- handlerRegistry.register("*", new ProxyRequestHandler(targetHost, executor, connPool));
-
- ProxyServiceHandler serverProtocolHandler = new ProxyServiceHandler(
- inhttpproc,
- new ProxyIncomingConnectionReuseStrategy(),
- handlerRegistry);
- final ListeningIOReactor listeningIOReactor = new DefaultListeningIOReactor(
- new DefaultHttpServerIOEventHandlerFactory(serverProtocolHandler, ConnectionConfig.DEFAULT));
+ @Override
+ public void onDisconnect(final HttpConnection connection) {
+ System.out.println("[proxy->origin] connection closed " + connection);
+ }
- Thread t = new Thread(new Runnable() {
+ @Override
+ public void onError(final HttpConnection connection, final Exception ex) {
+ }
- @Override
- public void run() {
- try {
- connectingIOReactor.execute();
- } catch (InterruptedIOException ex) {
- System.err.println("Interrupted");
- } catch (IOException ex) {
- ex.printStackTrace();
- } finally {
- listeningIOReactor.shutdown(3, TimeUnit.SECONDS);
- }
- }
+ })
+ .setStreamListener(new Http1StreamListener() {
- });
- t.start();
- try {
- listeningIOReactor.listen(new InetSocketAddress(port));
- listeningIOReactor.execute();
- } catch (InterruptedIOException ex) {
- System.err.println("Interrupted");
- } catch (IOException ex) {
- ex.printStackTrace();
- } finally {
- connectingIOReactor.shutdown(3, TimeUnit.SECONDS);
- }
- }
+ @Override
+ public void onRequestHead(final HttpConnection connection, HttpRequest request) {
+ }
- static class ProxyHttpExchange {
+ @Override
+ public void onResponseHead(final HttpConnection connection, HttpResponse response) {
+ }
- private final ByteBuffer inBuffer;
- private final ByteBuffer outBuffer;
+ @Override
+ public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
+ System.out.println("[proxy<-origin] connection " + connection +
+ (keepAlive ? " kept alive" : " cannot be kept alive"));
+ }
- private volatile String id;
- private volatile HttpHost target;
- private volatile HttpAsyncExchange responseTrigger;
- private volatile IOControl originIOControl;
- private volatile IOControl clientIOControl;
- private volatile ClassicHttpRequest request;
- private volatile boolean requestReceived;
- private volatile ClassicHttpResponse response;
- private volatile boolean responseReceived;
- private volatile Exception ex;
+ })
+ .create();
- public ProxyHttpExchange() {
- super();
- this.inBuffer = ByteBuffer.allocateDirect(10240);
- this.outBuffer = ByteBuffer.allocateDirect(10240);
- }
+ final ProxyConnPool connPool = new ProxyConnPool(requester, 0);
+ connPool.setMaxTotal(100);
+ connPool.setDefaultMaxPerRoute(20);
- public ByteBuffer getInBuffer() {
- return this.inBuffer;
- }
+ final HttpAsyncServer server = ServerBootstrap.bootstrap()
+ .setIOReactorConfig(config)
+ .setConnectionListener(new ConnectionListener() {
+
+ @Override
+ public void onConnect(final HttpConnection connection) {
+ System.out.println("[client->proxy] connection open " + connection);
+ }
- public ByteBuffer getOutBuffer() {
- return this.outBuffer;
- }
+ @Override
+ public void onDisconnect(final HttpConnection connection) {
+ System.out.println("[client->proxy] connection closed " + connection);
+ }
- public String getId() {
- return this.id;
- }
+ @Override
+ public void onError(final HttpConnection connection, final Exception ex) {
+ }
- public void setId(final String id) {
- this.id = id;
- }
+ })
+ .setStreamListener(new Http1StreamListener() {
- public HttpHost getTarget() {
- return this.target;
- }
+ @Override
+ public void onRequestHead(final HttpConnection connection, HttpRequest request) {
+ }
- public void setTarget(final HttpHost target) {
- this.target = target;
- }
+ @Override
+ public void onResponseHead(final HttpConnection connection, HttpResponse response) {
+ }
- public ClassicHttpRequest getRequest() {
- return this.request;
- }
+ @Override
+ public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
+ System.out.println("[client<-proxy] connection " + connection +
+ (keepAlive ? " kept alive" : " cannot be kept alive"));
+ }
- public void setRequest(final ClassicHttpRequest request) {
- this.request = request;
- }
+ })
+ .register("*", new Supplier<AsyncServerExchangeHandler>() {
- public ClassicHttpResponse getResponse() {
- return this.response;
- }
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new IncomingExchangeHandler(targetHost, connPool);
+ }
- public void setResponse(final ClassicHttpResponse response) {
- this.response = response;
- }
+ })
+ .create();
- public HttpAsyncExchange getResponseTrigger() {
- return this.responseTrigger;
- }
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ System.out.println("Reverse proxy shutting down");
+ server.shutdown(5, TimeUnit.SECONDS);
+ requester.shutdown(5, TimeUnit.SECONDS);
+ }
+ });
- public void setResponseTrigger(final HttpAsyncExchange responseTrigger) {
- this.responseTrigger = responseTrigger;
- }
+ requester.start();
+ server.start();
+ server.listen(new InetSocketAddress(port));
- public IOControl getClientIOControl() {
- return this.clientIOControl;
- }
+ server.awaitShutdown(Integer.MAX_VALUE, TimeUnit.DAYS);
+ }
- public void setClientIOControl(final IOControl clientIOControl) {
- this.clientIOControl = clientIOControl;
- }
+ private static class ProxyBuffer extends ExpandableBuffer {
- public IOControl getOriginIOControl() {
- return this.originIOControl;
+ ProxyBuffer(int buffersize) {
+ super(buffersize, HeapByteBufferAllocator.INSTANCE);
}
- public void setOriginIOControl(final IOControl originIOControl) {
- this.originIOControl = originIOControl;
+ void put(final ByteBuffer src) {
+ setInputMode();
+ int requiredCapacity = buffer().position() + src.remaining();
+ ensureCapacity(requiredCapacity);
+ buffer().put(src);
}
- public boolean isRequestReceived() {
- return this.requestReceived;
+ int write(final DataStreamChannel channel) throws IOException {
+ setOutputMode();
+ if (buffer().hasRemaining()) {
+ return channel.write(buffer());
+ } else {
+ return 0;
+ }
}
- public void setRequestReceived() {
- this.requestReceived = true;
- }
+ }
- public boolean isResponseReceived() {
- return this.responseReceived;
- }
+ private static final AtomicLong COUNT = new AtomicLong(0);
- public void setResponseReceived() {
- this.responseReceived = true;
- }
+ private static class ProxyExchangeState {
- public Exception getException() {
- return this.ex;
- }
+ final String id;
- public void setException(final Exception ex) {
- this.ex = ex;
- }
+ ProxyBuffer inBuf;
+ ProxyBuffer outBuf;
+ HttpRequest request;
+ HttpResponse response;
+ boolean inputEnd;
+ boolean outputEnd;
+ ResponseChannel responseMessageChannel;
+ CapacityChannel requestCapacityChannel;
+ CapacityChannel responseCapacityChannel;
+ DataStreamChannel requestDataChannel;
+ DataStreamChannel responseDataChannel;
- public void reset() {
- this.inBuffer.clear();
- this.outBuffer.clear();
- this.target = null;
- this.id = null;
- this.responseTrigger = null;
- this.clientIOControl = null;
- this.originIOControl = null;
- this.request = null;
- this.requestReceived = false;
- this.response = null;
- this.responseReceived = false;
- this.ex = null;
+ ProxyExchangeState() {
+ this.id = String.format("%08X", COUNT.getAndIncrement());
}
}
- static class ProxyRequestHandler implements HttpAsyncRequestHandler<ProxyHttpExchange> {
-
- private final HttpHost target;
- private final HttpAsyncRequester executor;
- private final BasicNIOConnPool connPool;
- private final AtomicLong counter;
-
- public ProxyRequestHandler(
- final HttpHost target,
- final HttpAsyncRequester executor,
- final BasicNIOConnPool connPool) {
- super();
- this.target = target;
- this.executor = executor;
- this.connPool = connPool;
- this.counter = new AtomicLong(1);
- }
-
- @Override
- public HttpAsyncRequestConsumer<ProxyHttpExchange> processRequest(
- final ClassicHttpRequest request,
- final HttpContext context) {
- ProxyHttpExchange httpExchange = (ProxyHttpExchange) context.getAttribute("http-exchange");
- if (httpExchange == null) {
- httpExchange = new ProxyHttpExchange();
- context.setAttribute("http-exchange", httpExchange);
- }
- synchronized (httpExchange) {
- httpExchange.reset();
- String id = String.format("%08X", this.counter.getAndIncrement());
- httpExchange.setId(id);
- httpExchange.setTarget(this.target);
- return new ProxyRequestConsumer(httpExchange, this.executor, this.connPool);
- }
- }
-
- @Override
- public void handle(
- final ProxyHttpExchange httpExchange,
- final HttpAsyncExchange responseTrigger,
- final HttpContext context) throws HttpException, IOException {
- synchronized (httpExchange) {
- Exception ex = httpExchange.getException();
- if (ex != null) {
- System.out.println("[client<-proxy] " + httpExchange.getId() + " " + ex);
- int status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
- ClassicHttpResponse response = new BasicClassicHttpResponse(status);
- String message = ex.getMessage();
- if (message == null) {
- message = "Unexpected error";
- }
- response.setEntity(new NStringEntity(message, ContentType.DEFAULT_TEXT));
- responseTrigger.submitResponse(new BasicAsyncResponseProducer(response));
- System.out.println("[client<-proxy] " + httpExchange.getId() + " error response triggered");
- }
- ClassicHttpResponse response = httpExchange.getResponse();
- if (response != null) {
- responseTrigger.submitResponse(new ProxyResponseProducer(httpExchange));
- System.out.println("[client<-proxy] " + httpExchange.getId() + " response triggered");
- }
- // No response yet.
- httpExchange.setResponseTrigger(responseTrigger);
- }
- }
+ private static final int INIT_BUFFER_SIZE = 4096;
- }
+ private static class IncomingExchangeHandler implements AsyncServerExchangeHandler {
- static class ProxyRequestConsumer implements HttpAsyncRequestConsumer<ProxyHttpExchange> {
+ private final HttpHost targetHost;
+ private final ProxyConnPool connPool;
+ private final AtomicBoolean consistent;
+ private final AtomicReference<BasicNIOPoolEntry> poolEntryRef;
+ private final ProxyExchangeState exchangeState;
- private final ProxyHttpExchange httpExchange;
- private final HttpAsyncRequester executor;
- private final BasicNIOConnPool connPool;
-
- private volatile boolean completed;
-
- public ProxyRequestConsumer(
- final ProxyHttpExchange httpExchange,
- final HttpAsyncRequester executor,
- final BasicNIOConnPool connPool) {
+ IncomingExchangeHandler(final HttpHost targetHost, final ProxyConnPool connPool) {
super();
- this.httpExchange = httpExchange;
- this.executor = executor;
+ this.targetHost = targetHost;
this.connPool = connPool;
+ this.consistent = new AtomicBoolean(true);
+ this.poolEntryRef = new AtomicReference<>(null);
+ this.exchangeState = new ProxyExchangeState();
}
@Override
- public void close() throws IOException {
+ public void setContext(final HttpContext context) {
}
@Override
- public void requestReceived(final ClassicHttpRequest request) {
- synchronized (this.httpExchange) {
- System.out.println("[client->proxy] " + this.httpExchange.getId() + " " + request.getMethod() + " " + request.getPath());
- this.httpExchange.setRequest(request);
- this.executor.execute(
- new ProxyRequestProducer(this.httpExchange),
- new ProxyResponseConsumer(this.httpExchange),
- this.connPool);
- }
+ public void verify(
+ final HttpRequest request,
+ final EntityDetails entityDetails,
+ final ExpectationChannel expectationChannel) throws HttpException, IOException {
+ expectationChannel.sendContinue();
}
@Override
- public void consumeContent(
- final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
- synchronized (this.httpExchange) {
- this.httpExchange.setClientIOControl(ioctrl);
- // Receive data from the client
- ByteBuffer buf = this.httpExchange.getInBuffer();
- int n = decoder.read(buf);
- System.out.println("[client->proxy] " + this.httpExchange.getId() + " " + n + " bytes read");
- if (decoder.isCompleted()) {
- System.out.println("[client->proxy] " + this.httpExchange.getId() + " content fully read");
- }
- // If the buffer is full, suspend client input until there is free
- // space in the buffer
- if (!buf.hasRemaining()) {
- ioctrl.suspendInput();
- System.out.println("[client->proxy] " + this.httpExchange.getId() + " suspend client input");
- }
- // If there is some content in the input buffer make sure origin
- // output is active
- if (buf.position() > 0) {
- if (this.httpExchange.getOriginIOControl() != null) {
- this.httpExchange.getOriginIOControl().requestOutput();
- System.out.println("[client->proxy] " + this.httpExchange.getId() + " request origin output");
- }
- }
- }
- }
+ public void handleRequest(
+ final HttpRequest incomingRequest,
+ final EntityDetails entityDetails,
+ final ResponseChannel responseChannel) throws HttpException, IOException {
- @Override
- public void requestCompleted(final HttpContext context) {
- synchronized (this.httpExchange) {
- this.completed = true;;
- System.out.println("[client->proxy] " + this.httpExchange.getId() + " request completed");
- this.httpExchange.setRequestReceived();
- if (this.httpExchange.getOriginIOControl() != null) {
- this.httpExchange.getOriginIOControl().requestOutput();
- System.out.println("[client->proxy] " + this.httpExchange.getId() + " request origin output");
- }
+ synchronized (exchangeState) {
+ System.out.println("[client->proxy] " + exchangeState.id + " " +
+ incomingRequest.getMethod() + " " + incomingRequest.getPath());
+ exchangeState.request = incomingRequest;
+ exchangeState.inputEnd = entityDetails == null;
+ exchangeState.responseMessageChannel = responseChannel;
}
- }
- @Override
- public Exception getException() {
- return null;
- }
+ System.out.println("[proxy->origin] " + exchangeState.id + " request connection to " + targetHost);
- @Override
- public ProxyHttpExchange getResult() {
- return this.httpExchange;
- }
+ connPool.lease(targetHost, null, 10, TimeUnit.SECONDS, new FutureCallback<BasicNIOPoolEntry>() {
- @Override
- public boolean isDone() {
- return this.completed;
- }
+ @Override
+ public void completed(final BasicNIOPoolEntry poolEntry) {
+ poolEntryRef.set(poolEntry);
+ IOSession iosession = poolEntry.getConnection();
+ System.out.println("[proxy->origin] " + exchangeState.id + " connection leased: " + iosession.getHandler());
+ ClientEndpoint clientEndpoint = new ClientEndpointImpl(iosession);
+ clientEndpoint.execute(new OutgoingExchangeHandler(exchangeState), null);
+ }
- @Override
- public void failed(final Exception ex) {
- System.out.println("[client->proxy] " + ex.toString());
- }
+ @Override
+ public void failed(final Exception cause) {
+ HttpResponse outgoingResponse = new BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE);
+ outgoingResponse.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
+ exchangeState.response = outgoingResponse;
- }
+ ByteBuffer msg = StandardCharsets.US_ASCII.encode(CharBuffer.wrap(cause.getMessage()));
+ exchangeState.outBuf = new ProxyBuffer(1024);
+ exchangeState.outBuf.put(msg);
+ exchangeState.outputEnd = true;
- static class ProxyRequestProducer implements HttpAsyncRequestProducer {
+ System.out.println("[client<-proxy] " + exchangeState.id + " status " + outgoingResponse.getCode());
- private final ProxyHttpExchange httpExchange;
+ try {
+ EntityDetails entityDetails = new BasicEntityDetails(msg.remaining(), ContentType.TEXT_PLAIN);
+ responseChannel.sendResponse(outgoingResponse, entityDetails);
+ } catch (HttpException | IOException ignore) {
+ }
+ }
- public ProxyRequestProducer(final ProxyHttpExchange httpExchange) {
- super();
- this.httpExchange = httpExchange;
- }
+ @Override
+ public void cancelled() {
+ failed(new InterruptedIOException());
+ }
- @Override
- public void close() throws IOException {
- }
+ });
- @Override
- public HttpHost getTarget() {
- synchronized (this.httpExchange) {
- return this.httpExchange.getTarget();
- }
}
@Override
- public ClassicHttpRequest generateRequest() {
- synchronized (this.httpExchange) {
- ClassicHttpRequest request = this.httpExchange.getRequest();
- System.out.println("[proxy->origin] " + this.httpExchange.getId() + " " + request.getMethod() + " " + request.getPath());
- // Rewrite request!!!!
- BasicClassicHttpRequest newREquest = new BasicClassicHttpRequest(request.getMethod(), request.getPath());
- newREquest.setEntity(request.getEntity());
- return newREquest;
+ public void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
+ synchronized (exchangeState) {
+ exchangeState.requestCapacityChannel = capacityChannel;
+ int capacity = exchangeState.inBuf != null ? exchangeState.inBuf.capacity() : INIT_BUFFER_SIZE;
+ if (capacity > 0) {
+ System.out.println("[client<-proxy] " + exchangeState.id + " input capacity: " + capacity);
+ capacityChannel.update(capacity);
+ }
}
}
@Override
- public void produceContent(
- final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
- synchronized (this.httpExchange) {
- this.httpExchange.setOriginIOControl(ioctrl);
- // Send data to the origin server
- ByteBuffer buf = this.httpExchange.getInBuffer();
- buf.flip();
- int n = encoder.write(buf);
- buf.compact();
- System.out.println("[proxy->origin] " + this.httpExchange.getId() + " " + n + " bytes written");
- // If there is space in the buffer and the message has not been
- // transferred, make sure the client is sending more data
- if (buf.hasRemaining() && !this.httpExchange.isRequestReceived()) {
- if (this.httpExchange.getClientIOControl() != null) {
- this.httpExchange.getClientIOControl().requestInput();
- System.out.println("[proxy->origin] " + this.httpExchange.getId() + " request client input");
+ public int consume(final ByteBuffer src) throws IOException {
+ synchronized (exchangeState) {
+ System.out.println("[client->proxy] " + exchangeState.id + " " + src.remaining() + " bytes received");
+ DataStreamChannel dataChannel = exchangeState.requestDataChannel;
+ if (dataChannel != null && exchangeState.inBuf != null) {
+ if (exchangeState.inBuf.hasData()) {
+ int bytesWritten = exchangeState.inBuf.write(dataChannel);
+ System.out.println("[proxy->origin] " + exchangeState.id + " " + bytesWritten + " bytes sent");
+ }
+ if (!exchangeState.inBuf.hasData()) {
+ int bytesWritten = dataChannel.write(src);
+ System.out.println("[proxy->origin] " + exchangeState.id + " " + bytesWritten + " bytes sent");
}
}
- if (buf.position() == 0) {
- if (this.httpExchange.isRequestReceived()) {
- encoder.complete();
- System.out.println("[proxy->origin] " + this.httpExchange.getId() + " content fully written");
- } else {
- // Input buffer is empty. Wait until the client fills up
- // the buffer
- ioctrl.suspendOutput();
- System.out.println("[proxy->origin] " + this.httpExchange.getId() + " suspend origin output");
+ if (src.hasRemaining()) {
+ if (exchangeState.inBuf == null) {
+ exchangeState.inBuf = new ProxyBuffer(INIT_BUFFER_SIZE);
}
+ exchangeState.inBuf.put(src);
+ }
+ int capacity = exchangeState.inBuf != null ? exchangeState.inBuf.capacity() : INIT_BUFFER_SIZE;
+ System.out.println("[client<-proxy] " + exchangeState.id + " input capacity: " + capacity);
+ if (dataChannel != null) {
+ dataChannel.requestOutput();
}
+ return capacity;
}
}
@Override
- public void requestCompleted(final HttpContext context) {
- synchronized (this.httpExchange) {
- System.out.println("[proxy->origin] " + this.httpExchange.getId() + " request completed");
+ public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+ synchronized (exchangeState) {
+ System.out.println("[client->proxy] " + exchangeState.id + " end of input");
+ exchangeState.inputEnd = true;
+ DataStreamChannel dataChannel = exchangeState.requestDataChannel;
+ if (dataChannel != null && (exchangeState.inBuf == null || !exchangeState.inBuf.hasData())) {
+ System.out.println("[proxy->origin] " + exchangeState.id + " end of output");
+ dataChannel.endStream();
+ }
}
}
@Override
- public boolean isRepeatable() {
- return false;
- }
-
- @Override
- public void resetRequest() {
+ public int available() {
+ synchronized (exchangeState) {
+ int available = exchangeState.outBuf != null ? exchangeState.outBuf.length() : 0;
+ System.out.println("[client<-proxy] " + exchangeState.id + " output available: " + available);
+ return available;
+ }
}
@Override
- public void failed(final Exception ex) {
- System.out.println("[proxy->origin] " + ex.toString());
- }
-
- }
-
- static class ProxyResponseConsumer implements HttpAsyncResponseConsumer<ProxyHttpExchange> {
-
- private final ProxyHttpExchange httpExchange;
+ public void produce(final DataStreamChannel channel) throws IOException {
+ synchronized (exchangeState) {
+ System.out.println("[client<-proxy] " + exchangeState.id + " produce output");
+ exchangeState.responseDataChannel = channel;
- private volatile boolean completed;
-
- public ProxyResponseConsumer(final ProxyHttpExchange httpExchange) {
- super();
- this.httpExchange = httpExchange;
+ if (exchangeState.outBuf != null) {
+ if (exchangeState.outBuf.hasData()) {
+ int bytesWritten = exchangeState.outBuf.write(channel);
+ System.out.println("[client<-proxy] " + exchangeState.id + " " + bytesWritten + " bytes sent");
+ }
+ if (exchangeState.outputEnd && !exchangeState.outBuf.hasData()) {
+ channel.endStream();
+ System.out.println("[client<-proxy] " + exchangeState.id + " end of output");
+ }
+ if (!exchangeState.outputEnd) {
+ CapacityChannel capacityChannel = exchangeState.responseCapacityChannel;
+ if (capacityChannel != null) {
+ int capacity = exchangeState.outBuf.capacity();
+ if (capacity > 0) {
+ System.out.println("[proxy->origin] " + exchangeState.id + " input capacity: " + capacity);
+ capacityChannel.update(capacity);
+ }
+ }
+ }
+ }
+ }
}
@Override
- public void close() throws IOException {
+ public void failed(final Exception cause) {
+ System.out.println("[client<-proxy] " + exchangeState.id + " error: " + cause.getMessage());
+ cause.printStackTrace(System.out);
+ consistent.set(false);
+ releaseResources();
}
@Override
- public void responseReceived(final ClassicHttpResponse response) {
- synchronized (this.httpExchange) {
- System.out.println("[proxy<-origin] " + this.httpExchange.getId() + " " + response.getCode());
- this.httpExchange.setResponse(response);
- HttpAsyncExchange responseTrigger = this.httpExchange.getResponseTrigger();
- if (responseTrigger != null && !responseTrigger.isCompleted()) {
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " response triggered");
- responseTrigger.submitResponse(new ProxyResponseProducer(this.httpExchange));
- }
+ public void releaseResources() {
+ BasicNIOPoolEntry poolEntry = poolEntryRef.getAndSet(null);
+ if (poolEntry != null) {
+ System.out.println("[proxy->origin] " + exchangeState.id + " releasing connection");
+ connPool.release(poolEntry, consistent.get());
+ }
+ synchronized (exchangeState) {
+ exchangeState.responseMessageChannel = null;
+ exchangeState.responseDataChannel = null;
+ exchangeState.requestCapacityChannel = null;
}
}
+ }
+
+ private final static Set<String> HOP_BY_HOP = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ HttpHeaders.CONTENT_LENGTH.toLowerCase(Locale.ROOT),
+ HttpHeaders.TRANSFER_ENCODING.toLowerCase(Locale.ROOT),
+ HttpHeaders.CONNECTION.toLowerCase(Locale.ROOT),
+ "Keep-Alive".toLowerCase(Locale.ROOT),
+ "Proxy-Authenticate".toLowerCase(Locale.ROOT),
+ HttpHeaders.TE.toLowerCase(Locale.ROOT),
+ HttpHeaders.TRAILER.toLowerCase(Locale.ROOT),
+ HttpHeaders.UPGRADE.toLowerCase(Locale.ROOT))));
+
+ private static class OutgoingExchangeHandler implements AsyncClientExchangeHandler {
+
+ private final ProxyExchangeState exchangeState;
+
+ OutgoingExchangeHandler(final ProxyExchangeState exchangeState) {
+ this.exchangeState = exchangeState;
+ }
+
@Override
- public void consumeContent(
- final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
- synchronized (this.httpExchange) {
- this.httpExchange.setOriginIOControl(ioctrl);
- // Receive data from the origin
- ByteBuffer buf = this.httpExchange.getOutBuffer();
- int n = decoder.read(buf);
- System.out.println("[proxy<-origin] " + this.httpExchange.getId() + " " + n + " bytes read");
- if (decoder.isCompleted()) {
- System.out.println("[proxy<-origin] " + this.httpExchange.getId() + " content fully read");
- }
- // If the buffer is full, suspend origin input until there is free
- // space in the buffer
- if (!buf.hasRemaining()) {
- ioctrl.suspendInput();
- System.out.println("[proxy<-origin] " + this.httpExchange.getId() + " suspend origin input");
- }
- // If there is some content in the input buffer make sure client
- // output is active
- if (buf.position() > 0) {
- if (this.httpExchange.getClientIOControl() != null) {
- this.httpExchange.getClientIOControl().requestOutput();
- System.out.println("[proxy<-origin] " + this.httpExchange.getId() + " request client output");
+ public void produceRequest(
+ final RequestChannel channel) throws HttpException, IOException {
+ synchronized (exchangeState) {
+ HttpRequest incomingRequest = exchangeState.request;
+ HttpRequest outgoingRequest = new BasicHttpRequest(incomingRequest.getMethod(), incomingRequest.getPath());
+ for (Iterator<Header> it = incomingRequest.headerIterator(); it.hasNext(); ) {
+ Header header = it.next();
+ if (!HOP_BY_HOP.contains(header.getName().toLowerCase(Locale.ROOT))) {
+ outgoingRequest.addHeader(header);
}
}
+
+ System.out.println("[proxy->origin] " + exchangeState.id + " " +
+ outgoingRequest.getMethod() + " " + outgoingRequest.getPath());
+
+ channel.sendRequest(
+ outgoingRequest,
+ !exchangeState.inputEnd ? new LazyEntityDetails(outgoingRequest) : null);
}
}
@Override
- public void responseCompleted(final HttpContext context) {
- synchronized (this.httpExchange) {
- if (this.completed) {
- return;
- }
- this.completed = true;
- System.out.println("[proxy<-origin] " + this.httpExchange.getId() + " response completed");
- this.httpExchange.setResponseReceived();
- if (this.httpExchange.getClientIOControl() != null) {
- this.httpExchange.getClientIOControl().requestOutput();
- System.out.println("[proxy<-origin] " + this.httpExchange.getId() + " request client output");
- }
+ public int available() {
+ synchronized (exchangeState) {
+ int available = exchangeState.inBuf != null ? exchangeState.inBuf.length() : 0;
+ System.out.println("[proxy->origin] " + exchangeState.id + " output available: " + available);
+ return available;
}
}
@Override
- public void failed(final Exception ex) {
- synchronized (this.httpExchange) {
- if (this.completed) {
- return;
- }
- this.completed = true;
- this.httpExchange.setException(ex);
- HttpAsyncExchange responseTrigger = this.httpExchange.getResponseTrigger();
- if (responseTrigger != null && !responseTrigger.isCompleted()) {
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " " + ex);
- int status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
- ClassicHttpResponse response = new BasicClassicHttpResponse(status);
- String message = ex.getMessage();
- if (message == null) {
- message = "Unexpected error";
+ public void produce(final DataStreamChannel channel) throws IOException {
+ synchronized (exchangeState) {
+ System.out.println("[proxy->origin] " + exchangeState.id + " produce output");
+ exchangeState.requestDataChannel = channel;
+ if (exchangeState.inBuf != null) {
+ if (exchangeState.inBuf.hasData()) {
+ int bytesWritten = exchangeState.inBuf.write(channel);
+ System.out.println("[proxy->origin] " + exchangeState.id + " " + bytesWritten + " bytes sent");
+ }
+ if (exchangeState.inputEnd && !exchangeState.inBuf.hasData()) {
+ channel.endStream();
+ System.out.println("[proxy->origin] " + exchangeState.id + " end of output");
+ }
+ if (!exchangeState.inputEnd) {
+ CapacityChannel capacityChannel = exchangeState.requestCapacityChannel;
+ if (capacityChannel != null) {
+ int capacity = exchangeState.inBuf.capacity();
+ if (capacity > 0) {
+ System.out.println("[client<-proxy] " + exchangeState.id + " input capacity: " + capacity);
+ capacityChannel.update(capacity);
+ }
+ }
}
- response.setEntity(new NStringEntity(message, ContentType.DEFAULT_TEXT));
- responseTrigger.submitResponse(new BasicAsyncResponseProducer(response));
}
}
}
@Override
- public boolean cancel() {
- synchronized (this.httpExchange) {
- if (this.completed) {
- return false;
+ public void consumeResponse(
+ final HttpResponse incomingResponse,
+ final EntityDetails entityDetails) throws HttpException, IOException {
+ synchronized (exchangeState) {
+ System.out.println("[proxy<-origin] " + exchangeState.id + " status " + incomingResponse.getCode());
+
+ HttpResponse outgoingResponse = new BasicHttpResponse(incomingResponse.getCode());
+ for (Iterator<Header> it = incomingResponse.headerIterator(); it.hasNext(); ) {
+ Header header = it.next();
+ if (!HOP_BY_HOP.contains(header.getName().toLowerCase(Locale.ROOT))) {
+ outgoingResponse.addHeader(header);
+ }
}
- failed(new InterruptedIOException("Cancelled"));
- return true;
- }
- }
- @Override
- public ProxyHttpExchange getResult() {
- return this.httpExchange;
- }
+ exchangeState.response = outgoingResponse;
+ exchangeState.outputEnd = entityDetails == null;
- @Override
- public Exception getException() {
- return null;
- }
+ ResponseChannel responseChannel = exchangeState.responseMessageChannel;
+ responseChannel.sendResponse(
+ outgoingResponse,
+ !exchangeState.outputEnd ? new LazyEntityDetails(outgoingResponse) : null);
- @Override
- public boolean isDone() {
- return this.completed;
- }
-
- }
-
- static class ProxyResponseProducer implements HttpAsyncResponseProducer {
-
- private final ProxyHttpExchange httpExchange;
-
- public ProxyResponseProducer(final ProxyHttpExchange httpExchange) {
- super();
- this.httpExchange = httpExchange;
- }
-
- @Override
- public void close() throws IOException {
- this.httpExchange.reset();
+ System.out.println("[client<-proxy] " + exchangeState.id + " status " + outgoingResponse.getCode());
+ }
}
@Override
- public ClassicHttpResponse generateResponse() {
- synchronized (this.httpExchange) {
- ClassicHttpResponse response = this.httpExchange.getResponse();
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " " + response.getCode());
- // Rewrite response!!!!
- BasicClassicHttpResponse r = new BasicClassicHttpResponse(response.getCode());
- r.setEntity(response.getEntity());
- return r;
+ public void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
+ synchronized (exchangeState) {
+ exchangeState.responseCapacityChannel = capacityChannel;
+ int capacity = exchangeState.outBuf != null ? exchangeState.outBuf.capacity() : INIT_BUFFER_SIZE;
+ if (capacity > 0) {
+ System.out.println("[proxy->origin] " + exchangeState.id + " input capacity: " + capacity);
+ capacityChannel.update(capacity);
+ }
}
}
@Override
- public void produceContent(
- final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
- synchronized (this.httpExchange) {
- this.httpExchange.setClientIOControl(ioctrl);
- // Send data to the client
- ByteBuffer buf = this.httpExchange.getOutBuffer();
- buf.flip();
- int n = encoder.write(buf);
- buf.compact();
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " " + n + " bytes written");
- // If there is space in the buffer and the message has not been
- // transferred, make sure the origin is sending more data
- if (buf.hasRemaining() && !this.httpExchange.isResponseReceived()) {
- if (this.httpExchange.getOriginIOControl() != null) {
- this.httpExchange.getOriginIOControl().requestInput();
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " request origin input");
+ public int consume(final ByteBuffer src) throws IOException {
+ synchronized (exchangeState) {
+ System.out.println("[proxy<-origin] " + exchangeState.id + " " + src.remaining() + " bytes received");
+ DataStreamChannel dataChannel = exchangeState.responseDataChannel;
+ if (dataChannel != null && exchangeState.outBuf != null) {
+ if (exchangeState.outBuf.hasData()) {
+ int bytesWritten = exchangeState.outBuf.write(dataChannel);
+ System.out.println("[client<-proxy] " + exchangeState.id + " " + bytesWritten + " bytes sent");
+ }
+ if (!exchangeState.outBuf.hasData()) {
+ int bytesWritten = dataChannel.write(src);
+ System.out.println("[client<-proxy] " + exchangeState.id + " " + bytesWritten + " bytes sent");
}
}
- if (buf.position() == 0) {
- if (this.httpExchange.isResponseReceived()) {
- encoder.complete();
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " content fully written");
- } else {
- // Input buffer is empty. Wait until the origin fills up
- // the buffer
- ioctrl.suspendOutput();
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " suspend client output");
+ if (src.hasRemaining()) {
+ if (exchangeState.outBuf == null) {
+ exchangeState.outBuf = new ProxyBuffer(INIT_BUFFER_SIZE);
}
+ exchangeState.outBuf.put(src);
}
+ int capacity = exchangeState.outBuf != null ? exchangeState.outBuf.capacity() : INIT_BUFFER_SIZE;
+ System.out.println("[proxy->origin] " + exchangeState.id + " input capacity: " + capacity);
+ if (dataChannel != null) {
+ dataChannel.requestOutput();
+ }
+ return capacity;
}
}
@Override
- public void responseCompleted(final HttpContext context) {
- synchronized (this.httpExchange) {
- System.out.println("[client<-proxy] " + this.httpExchange.getId() + " response completed");
+ public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+ synchronized (exchangeState) {
+ System.out.println("[proxy<-origin] " + exchangeState.id + " end of input");
+ exchangeState.outputEnd = true;
+ DataStreamChannel dataChannel = exchangeState.responseDataChannel;
+ if (dataChannel != null && (exchangeState.outBuf == null || !exchangeState.outBuf.hasData())) {
+ System.out.println("[client<-proxy] " + exchangeState.id + " end of output");
+ dataChannel.endStream();
+ }
}
}
@Override
- public void failed(final Exception ex) {
- System.out.println("[client<-proxy] " + ex.toString());
+ public void cancel() {
+ releaseResources();
}
- }
-
- static class ProxyIncomingConnectionReuseStrategy extends DefaultConnectionReuseStrategy {
-
@Override
- public boolean keepAlive(final HttpRequest request, final HttpResponse response, final HttpContext context) {
- NHttpConnection conn = (NHttpConnection) context.getAttribute(
- HttpCoreContext.HTTP_CONNECTION);
- boolean keepAlive = super.keepAlive(request, response, context);
- if (keepAlive) {
- System.out.println("[client->proxy] connection kept alive " + conn);
- }
- return keepAlive;
- }
+ public void failed(final Exception cause) {
+ System.out.println("[client<-proxy] " + exchangeState.id + " error: " + cause.getMessage());
+ cause.printStackTrace(System.out);
+ synchronized (exchangeState) {
+ if (exchangeState.response == null) {
+ int status = cause instanceof IOException ? HttpStatus.SC_SERVICE_UNAVAILABLE : HttpStatus.SC_INTERNAL_SERVER_ERROR;
+ HttpResponse outgoingResponse = new BasicHttpResponse(status);
+ outgoingResponse.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
+ exchangeState.response = outgoingResponse;
- };
+ ByteBuffer msg = StandardCharsets.US_ASCII.encode(CharBuffer.wrap(cause.getMessage()));
+ exchangeState.outBuf = new ProxyBuffer(1024);
+ exchangeState.outBuf.put(msg);
+ exchangeState.outputEnd = true;
- static class ProxyOutgoingConnectionReuseStrategy extends DefaultConnectionReuseStrategy {
+ System.out.println("[client<-proxy] " + exchangeState.id + " status " + outgoingResponse.getCode());
- @Override
- public boolean keepAlive(final HttpRequest request, final HttpResponse response, final HttpContext context) {
- NHttpConnection conn = (NHttpConnection) context.getAttribute(
- HttpCoreContext.HTTP_CONNECTION);
- boolean keepAlive = super.keepAlive(request, response, context);
- if (keepAlive) {
- System.out.println("[proxy->origin] connection kept alive " + conn);
+ try {
+ EntityDetails entityDetails = new BasicEntityDetails(msg.remaining(), ContentType.TEXT_PLAIN);
+ exchangeState.responseMessageChannel.sendResponse(outgoingResponse, entityDetails);
+ } catch (HttpException | IOException ignore) {
+ }
+ } else {
+ exchangeState.outputEnd = true;
+ }
+ releaseResources();
}
- return keepAlive;
- }
-
- };
-
- static class ProxyServiceHandler extends HttpAsyncService {
-
- public ProxyServiceHandler(
- final HttpProcessor httpProcessor,
- final ConnectionReuseStrategy reuseStrategy,
- final HttpAsyncRequestHandlerMapper handlerResolver) {
- super(httpProcessor, reuseStrategy, null, handlerResolver, null);
- }
-
- @Override
- protected void log(final Exception ex) {
- ex.printStackTrace();
- }
-
- @Override
- public void connected(final NHttpServerConnection conn) {
- System.out.println("[client->proxy] connection open " + conn);
- super.connected(conn);
}
@Override
- public void closed(final NHttpServerConnection conn) {
- System.out.println("[client->proxy] connection closed " + conn);
- super.closed(conn);
- }
-
- }
-
- static class ProxyClientProtocolHandler extends HttpAsyncRequestExecutor {
-
- public ProxyClientProtocolHandler() {
- super(HttpAsyncRequestExecutor.DEFAULT_WAIT_FOR_CONTINUE, new ProxyOutgoingConnectionReuseStrategy(), null);
- }
-
- @Override
- protected void log(final Exception ex) {
- ex.printStackTrace();
- }
-
- @Override
- public void connected(final NHttpClientConnection conn,
- final Object attachment) throws IOException, HttpException {
- System.out.println("[proxy->origin] connection open " + conn);
- super.connected(conn, attachment);
- }
-
- @Override
- public void closed(final NHttpClientConnection conn) {
- System.out.println("[proxy->origin] connection closed " + conn);
- super.closed(conn);
+ public void releaseResources() {
+ synchronized (exchangeState) {
+ exchangeState.requestDataChannel = null;
+ exchangeState.responseCapacityChannel = null;
+ }
}
}
- static class ProxyConnPool extends BasicNIOConnPool {
+ private static class ProxyConnPool extends BasicNIOConnPool {
- public ProxyConnPool(
- final ConnectingIOReactor ioreactor,
+ ProxyConnPool(
+ final ConnectionInitiator connectionInitiator,
final int connectTimeout) {
- super(ioreactor, connectTimeout);
+ super(connectionInitiator, connectTimeout);
}
@Override
public void release(final BasicNIOPoolEntry entry, boolean reusable) {
- System.out.println("[proxy->origin] connection released " + entry.getConnection());
super.release(entry, reusable);
StringBuilder buf = new StringBuilder();
PoolStats totals = getTotalStats();
Modified: httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/PrintVersionInfo.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/PrintVersionInfo.java?rev=1767339&r1=1767338&r2=1767339&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/PrintVersionInfo.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/PrintVersionInfo.java Mon Oct 31 17:33:27 2016
@@ -41,7 +41,6 @@ public class PrintVersionInfo {
/** A default list of module packages. */
private final static String[] MODULE_LIST = {
"org.apache.http", // HttpCore
- "org.apache.http.nio", // HttpCore NIO
"org.apache.http.client", // HttpClient
};
Copied: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java (from r1765384, httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/OoopsieRuntimeException.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java?p2=httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java&p1=httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/OoopsieRuntimeException.java&r1=1765384&r2=1767339&rev=1767339&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/OoopsieRuntimeException.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java Mon Oct 31 17:33:27 2016
@@ -27,12 +27,27 @@
package org.apache.hc.core5.http;
-public class OoopsieRuntimeException extends RuntimeException {
+/**
+ * Signals a misdirected request (the server is not authoritative to handle the request).
+ *
+ * @since 5.0
+ */
+public class MisdirectedRequestException extends ProtocolException {
- private static final long serialVersionUID = 662807254163212266L;
+ /**
+ * Creates an exception without a detail message.
+ */
+ public MisdirectedRequestException() {
+ super();
+ }
- public OoopsieRuntimeException() {
- super("Ooopsie!!!");
+ /**
+ * Creates an exception with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public MisdirectedRequestException(final String message) {
+ super(message);
}
}
Propchange: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java
------------------------------------------------------------------------------
svn:mime-type = text/plain