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 2018/08/26 15:34:11 UTC
[3/6] httpcomponents-core git commit: Removed HttpCore 4.x tutorual
sources
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c53af482/src/docbkx/nio.xml
----------------------------------------------------------------------
diff --git a/src/docbkx/nio.xml b/src/docbkx/nio.xml
deleted file mode 100644
index beb3963..0000000
--- a/src/docbkx/nio.xml
+++ /dev/null
@@ -1,2195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
- "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
-<!--
- ====================================================================
- 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.
- ====================================================================
-
--->
-<chapter id="nio">
- <title>Asynchronous I/O based on NIO</title>
- <para>
- Asynchronous I/O model may be more appropriate for those scenarios where raw data throughput
- is less important than the ability to handle thousands of simultaneous connections in
- a scalable, resource efficient manner. Asynchronous I/O is arguably more complex and usually
- requires a special care when dealing with large message payloads.
- </para>
- <section>
- <title>Differences from other I/O frameworks</title>
- <para>
- Solves similar problems as other frameworks, but has certain distinct features:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- minimalistic, optimized for data volume intensive protocols such as HTTP.
- </para>
- </listitem>
- <listitem>
- <para>
- efficient memory management: data consumer can read is only as much input data as it
- can process without having to allocate more memory.
- </para>
- </listitem>
- <listitem>
- <para>
- direct access to the NIO channels where possible.
- </para>
- </listitem>
- </itemizedlist>
- </section>
- <section>
- <title>I/O reactor</title>
- <para>
- HttpCore NIO is based on the Reactor pattern as described by Doug Lea. The purpose of I/O
- reactors is to react to I/O events and to dispatch event notifications to individual I/O
- sessions. The main idea of I/O reactor pattern is to break away from the one thread per
- connection model imposed by the classic blocking I/O model. The <interfacename>IOReactor
- </interfacename> interface represents an abstract object which implements the Reactor pattern.
- Internally, <interfacename>IOReactor</interfacename> implementations encapsulate
- functionality of the NIO <classname>java.nio.channels.Selector</classname>.
- </para>
- <para>
- I/O reactors usually employ a small number of dispatch threads (often as few as one) to
- dispatch I/O event notifications to a much greater number (often as many as several
- thousands) of I/O sessions or connections. It is generally recommended to have one dispatch
- thread per CPU core.
- </para>
- <programlisting><![CDATA[
-IOReactorConfig config = IOReactorConfig.DEFAULT;
-IOReactor ioreactor = new DefaultConnectingIOReactor(config);
-]]></programlisting>
- <section>
- <title>I/O dispatchers</title>
- <para>
- <interfacename>IOReactor</interfacename> implementations make use of the
- <interfacename>IOEventDispatch</interfacename> interface to notify clients of events
- pending for a particular session. All methods of the <interfacename>IOEventDispatch
- </interfacename> are executed on a dispatch thread of the I/O reactor. Therefore, it is
- important that processing that takes place in the event methods will not block the
- dispatch thread for too long, as the I/O reactor will be unable to react to other
- events.
- </para>
- <programlisting><![CDATA[
-IOReactor ioreactor = new DefaultConnectingIOReactor();
-
-IOEventDispatch eventDispatch = <...>
-ioreactor.execute(eventDispatch);
-]]></programlisting>
- <para>
- Generic I/O events as defined by the <interfacename>IOEventDispatch</interfacename>
- interface:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>connected</methodname>:</title>
- <para>
- Triggered when a new session has been created.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>inputReady</methodname>:</title>
- <para>
- Triggered when the session has pending input.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>outputReady</methodname>:</title>
- <para>
- Triggered when the session is ready for output.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>timeout</methodname>:</title>
- <para>
- Triggered when the session has timed out.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>disconnected</methodname>:</title>
- <para>
- Triggered when the session has been terminated.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- </section>
- <section>
- <title>I/O reactor shutdown</title>
- <para>
- The shutdown of I/O reactors is a complex process and may usually take a while to
- complete. I/O reactors will attempt to gracefully terminate all active I/O sessions and
- dispatch threads approximately within the specified grace period. If any of the I/O
- sessions fails to terminate correctly, the I/O reactor will forcibly shut down
- remaining sessions.
- </para>
- <programlisting><![CDATA[
-IOReactor ioreactor = <...>
-long gracePeriod = 3000L; // milliseconds
-ioreactor.shutdown(gracePeriod);
-]]></programlisting>
- <para>
- The <methodname>IOReactor#shutdown(long)</methodname> method is safe to call from any
- thread.
- </para>
- </section>
- <section>
- <title>I/O sessions</title>
- <para>
- The <interfacename>IOSession</interfacename> interface represents a sequence of
- logically related data exchanges between two end points. <interfacename>IOSession
- </interfacename> encapsulates functionality of NIO <classname>
- java.nio.channels.SelectionKey</classname> and <classname>
- java.nio.channels.SocketChannel</classname>. The channel associated with the
- <interfacename>IOSession</interfacename> can be used to read data from and write data
- to the session.
- </para>
- <programlisting><![CDATA[
-IOSession iosession = <...>
-ReadableByteChannel ch = (ReadableByteChannel) iosession.channel();
-ByteBuffer dst = ByteBuffer.allocate(2048);
-ch.read(dst);
-]]></programlisting>
- </section>
- <section>
- <title>I/O session state management</title>
- <para>
- I/O sessions are not bound to an execution thread, therefore one cannot use the context
- of the thread to store a session's state. All details about a particular session must
- be stored within the session itself.
- </para>
- <programlisting><![CDATA[
-IOSession iosession = <...>
-Object someState = <...>
-iosession.setAttribute("state", someState);
-...
-IOSession iosession = <...>
-Object currentState = iosession.getAttribute("state");
-]]></programlisting>
- <para>
- Please note that if several sessions make use of shared objects, access to those
- objects must be made thread-safe.
- </para>
- </section>
- <section>
- <title>I/O session event mask</title>
- <para>
- One can declare an interest in a particular type of I/O events for a particular I/O
- session by setting its event mask.
- </para>
- <programlisting><![CDATA[
-IOSession iosession = <...>
-iosession.setEventMask(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
-]]></programlisting>
- <para>
- One can also toggle <literal>OP_READ</literal> and <literal>OP_WRITE</literal> flags
- individually.
- </para>
- <programlisting><![CDATA[
-IOSession iosession = <...>
-iosession.setEvent(SelectionKey.OP_READ);
-iosession.clearEvent(SelectionKey.OP_READ);
-]]></programlisting>
- <para>
- Event notifications will not take place if the corresponding interest flag is not set.
- </para>
- </section>
- <section>
- <title>I/O session buffers</title>
- <para>
- Quite often I/O sessions need to maintain internal I/O buffers in order to transform
- input / output data prior to returning it to the consumer or writing it to the
- underlying channel. Memory management in HttpCore NIO is based on the fundamental
- principle that the data a consumer can read, is only as much input data as it can process
- without having to allocate more memory. That means, quite often some input data may
- remain unread in one of the internal or external session buffers. The I/O reactor can
- query the status of these session buffers, and make sure the consumer gets notified
- correctly as more data gets stored in one of the session buffers, thus allowing the
- consumer to read the remaining data once it is able to process it. I/O sessions can be
- made aware of the status of external session buffers using the <interfacename>
- SessionBufferStatus</interfacename> interface.
- </para>
- <programlisting><![CDATA[
-IOSession iosession = <...>
-SessionBufferStatus myBufferStatus = <...>
-iosession.setBufferStatus(myBufferStatus);
-iosession.hasBufferedInput();
-iosession.hasBufferedOutput();
-]]></programlisting>
- </section>
- <section>
- <title>I/O session shutdown</title>
- <para>
- One can close an I/O session gracefully by calling <methodname>IOSession#close()
- </methodname> allowing the session to be closed in an orderly manner or by calling
- <methodname>IOSession#shutdown()</methodname> to forcibly close the underlying channel.
- The distinction between two methods is of primary importance for those types of I/O
- sessions that involve some sort of a session termination handshake such as SSL/TLS
- connections.
- </para>
- </section>
- <section>
- <title>Listening I/O reactors</title>
- <para>
- <interfacename>ListeningIOReactor</interfacename> represents an I/O reactor capable of
- listening for incoming connections on one or several ports.
- </para>
- <programlisting><![CDATA[
-ListeningIOReactor ioreactor = <...>
-ListenerEndpoint ep1 = ioreactor.listen(new InetSocketAddress(8081) );
-ListenerEndpoint ep2 = ioreactor.listen(new InetSocketAddress(8082));
-ListenerEndpoint ep3 = ioreactor.listen(new InetSocketAddress(8083));
-// Wait until all endpoints are up
-ep1.waitFor();
-ep2.waitFor();
-ep3.waitFor();
-]]></programlisting>
- <para>
- Once an endpoint is fully initialized it starts accepting incoming connections and
- propagates I/O activity notifications to the <interfacename>IOEventDispatch
- </interfacename> instance.
- </para>
- <para>
- One can obtain a set of registered endpoints at runtime, query the status of an
- endpoint at runtime, and close it if desired.
- </para>
- <programlisting><![CDATA[
-ListeningIOReactor ioreactor = <...>
-
-Set<ListenerEndpoint> eps = ioreactor.getEndpoints();
-for (ListenerEndpoint ep: eps) {
- // Still active?
- System.out.println(ep.getAddress());
- if (ep.isClosed()) {
- // If not, has it terminated due to an exception?
- if (ep.getException() != null) {
- ep.getException().printStackTrace();
- }
- } else {
- ep.close();
- }
-}
-]]></programlisting>
- </section>
- <section>
- <title>Connecting I/O reactors</title>
- <para>
- <interfacename>ConnectingIOReactor</interfacename> represents an I/O reactor capable of
- establishing connections with remote hosts.
- </para>
- <programlisting><![CDATA[
-ConnectingIOReactor ioreactor = <...>
-
-SessionRequest sessionRequest = ioreactor.connect(
- new InetSocketAddress("www.google.com", 80),
- null, null, null);
-]]></programlisting>
- <para>
- Opening a connection to a remote host usually tends to be a time consuming process and
- may take a while to complete. One can monitor and control the process of session
- initialization by means of the <interfacename>SessionRequest</interfacename>interface.
- </para>
- <programlisting><![CDATA[
-// Make sure the request times out if connection
-// has not been established after 1 sec
-sessionRequest.setConnectTimeout(1000);
-// Wait for the request to complete
-sessionRequest.waitFor();
-// Has request terminated due to an exception?
-if (sessionRequest.getException() != null) {
- sessionRequest.getException().printStackTrace();
-}
-// Get hold of the new I/O session
-IOSession iosession = sessionRequest.getSession();
-]]></programlisting>
- <para>
- <interfacename>SessionRequest</interfacename> implementations are expected to be
- thread-safe. Session request can be aborted at any time by calling <methodname>
- IOSession#cancel()</methodname> from another thread of execution.
- </para>
- <programlisting><![CDATA[
-if (!sessionRequest.isCompleted()) {
- sessionRequest.cancel();
-}
-]]></programlisting>
- <para>
- One can pass several optional parameters to the <methodname>
- ConnectingIOReactor#connect()</methodname> method to exert a greater control over the
- process of session initialization.
- </para>
- <para>
- A non-null local socket address parameter can be used to bind the socket to a specific
- local address.
- </para>
- <programlisting><![CDATA[
-ConnectingIOReactor ioreactor = <...>
-
-SessionRequest sessionRequest = ioreactor.connect(
- new InetSocketAddress("www.google.com", 80),
- new InetSocketAddress("192.168.0.10", 1234),
- null, null);
-]]></programlisting>
- <para>
- One can provide an attachment object, which will be added to the new session's context
- upon initialization. This object can be used to pass an initial processing state to
- the protocol handler.
- </para>
- <programlisting><![CDATA[
-SessionRequest sessionRequest = ioreactor.connect(
- new InetSocketAddress("www.google.com", 80),
- null, new HttpHost("www.google.ru"), null);
-
-IOSession iosession = sessionRequest.getSession();
-HttpHost virtualHost = (HttpHost) iosession.getAttribute(
- IOSession.ATTACHMENT_KEY);
-]]></programlisting>
- <para>
- It is often desirable to be able to react to the completion of a session request
- asynchronously without having to wait for it, blocking the current thread of execution.
- One can optionally provide an implementation <interfacename>SessionRequestCallback
- </interfacename> interface to get notified of events related to session requests, such
- as request completion, cancellation, failure or timeout.
- </para>
- <programlisting><![CDATA[
-ConnectingIOReactor ioreactor = <...>
-
-SessionRequest sessionRequest = ioreactor.connect(
- new InetSocketAddress("www.google.com", 80), null, null,
- new SessionRequestCallback() {
-
- public void cancelled(SessionRequest request) {
- }
-
- public void completed(SessionRequest request) {
- System.out.println("new connection to " +
- request.getRemoteAddress());
- }
-
- public void failed(SessionRequest request) {
- if (request.getException() != null) {
- request.getException().printStackTrace();
- }
- }
-
- public void timeout(SessionRequest request) {
- }
-
- });
-]]></programlisting>
- </section>
- </section>
- <section>
- <title>I/O reactor configuration</title>
- <para>
- I/O reactors by default use system dependent configuration which in most cases should be
- sensible enough.
- </para>
- <programlisting><![CDATA[
-IOReactorConfig config = IOReactorConfig.DEFAULT;
-IOReactor ioreactor = new DefaultListeningIOReactor(config);
-]]></programlisting>
- <para>
- However in some cases custom settings may be necessary, for instance, in order to alter
- default socket properties and timeout values. One should rarely need to change other
- parameters.
- </para>
- <programlisting><![CDATA[
-IOReactorConfig config = IOReactorConfig.custom()
- .setTcpNoDelay(true)
- .setSoTimeout(5000)
- .setSoReuseAddress(true)
- .setConnectTimeout(5000)
- .build();
-IOReactor ioreactor = new DefaultListeningIOReactor(config);
-]]></programlisting>
- <section>
- <title>Queuing of I/O interest set operations</title>
- <para>
- Several older JRE implementations (primarily from IBM) include what Java API
- documentation refers to as a naive implementation of the <classname>
- java.nio.channels.SelectionKey</classname> class. The problem with <classname>
- java.nio.channels.SelectionKey</classname> in such JREs is that reading or writing
- of the I/O interest set may block indefinitely if the I/O selector is in the process
- of executing a select operation. HttpCore NIO can be configured to operate in a special
- mode wherein I/O interest set operations are queued and executed by on the dispatch
- thread only when the I/O selector is not engaged in a select operation.
- </para>
- <programlisting><![CDATA[
-IOReactorConfig config = IOReactorConfig.custom()
- .setInterestOpQueued(true)
- .build();
-]]></programlisting>
- </section>
- </section>
- <section>
- <title>I/O reactor exception handling</title>
- <para>
- Protocol specific exceptions as well as those I/O exceptions thrown in the course of
- interaction with the session's channel are to be expected and are to be dealt with by specific
- protocol handlers. These exceptions may result in termination of an individual session but
- should not affect the I/O reactor and all other active sessions. There are situations,
- however, when the I/O reactor itself encounters an internal problem such as an I/O
- exception in the underlying NIO classes or an unhandled runtime exception. Those types of
- exceptions are usually fatal and will cause the I/O reactor to shut down automatically.
- </para>
- <para>
- There is a possibility to override this behavior and prevent I/O reactors from shutting
- down automatically in case of a runtime exception or an I/O exception in internal classes.
- This can be accomplished by providing a custom implementation of the <interfacename>
- IOReactorExceptionHandler</interfacename> interface.
- </para>
- <programlisting><![CDATA[
-DefaultConnectingIOReactor ioreactor = <...>
-
-ioreactor.setExceptionHandler(new IOReactorExceptionHandler() {
-
- public boolean handle(IOException ex) {
- if (ex instanceof BindException) {
- // bind failures considered OK to ignore
- return true;
- }
- return false;
- }
-
- public boolean handle(RuntimeException ex) {
- if (ex instanceof UnsupportedOperationException) {
- // Unsupported operations considered OK to ignore
- return true;
- }
- return false;
- }
-
-});
-]]></programlisting>
- <para>
- One needs to be very careful about discarding exceptions indiscriminately. It is often much
- better to let the I/O reactor shut down itself cleanly and restart it rather than leaving
- it in an inconsistent or unstable state.
- </para>
- <section>
- <title>I/O reactor audit log</title>
- <para>
- If an I/O reactor is unable to automatically recover from an I/O or a runtime exception
- it will enter the shutdown mode. First off, it will close all active listeners and
- cancel all pending new session requests. Then it will attempt to close all active I/O
- sessions gracefully giving them some time to flush pending output data and terminate
- cleanly. Lastly, it will forcibly shut down those I/O sessions that still remain active
- after the grace period. This is a fairly complex process, where many things can fail at
- the same time and many different exceptions can be thrown in the course of the shutdown
- process. The I/O reactor will record all exceptions thrown during the shutdown process,
- including the original one that actually caused the shutdown in the first place, in an
- audit log. One can examine the audit log and decide whether it is safe to restart the
- I/O reactor.
- </para>
- <programlisting><![CDATA[
-DefaultConnectingIOReactor ioreactor = <...>
-
-// Give it 5 sec grace period
-ioreactor.shutdown(5000);
-List<ExceptionEvent> events = ioreactor.getAuditLog();
-for (ExceptionEvent event: events) {
- System.err.println("Time: " + event.getTimestamp());
- event.getCause().printStackTrace();
-}
-]]></programlisting>
- </section>
- </section>
- <section>
- <title>Non-blocking HTTP connections</title>
- <para>
- Effectively non-blocking HTTP connections are wrappers around <interfacename>IOSession
- </interfacename> with HTTP specific functionality. Non-blocking HTTP connections are
- stateful and not thread-safe. Input / output operations on non-blocking HTTP connections
- should be restricted to the dispatch events triggered by the I/O event dispatch thread.
- </para>
- <section>
- <title>Execution context of non-blocking HTTP connections</title>
- <para>
- Non-blocking HTTP connections are not bound to a particular thread of execution and
- therefore they need to maintain their own execution context. Each non-blocking HTTP
- connection has an <interfacename>HttpContext</interfacename> instance associated with
- it, which can be used to maintain a processing state. The <interfacename>HttpContext
- </interfacename> instance is thread-safe and can be manipulated from multiple threads.
- </para>
- <programlisting><![CDATA[
-DefaultNHttpClientConnection conn = <...>
-Object myStateObject = <...>
-
-HttpContext context = conn.getContext();
-context.setAttribute("state", myStateObject);
-]]></programlisting>
- </section>
- <section>
- <title>Working with non-blocking HTTP connections</title>
- <para>
- At any point of time one can obtain the request and response objects currently being
- transferred over the non-blocking HTTP connection. Any of these objects, or both, can
- be null if there is no incoming or outgoing message currently being transferred.
- </para>
- <programlisting><![CDATA[
-NHttpConnection conn = <...>
-
-HttpRequest request = conn.getHttpRequest();
-if (request != null) {
- System.out.println("Transferring request: " +
- request.getRequestLine());
-}
-HttpResponse response = conn.getHttpResponse();
-if (response != null) {
- System.out.println("Transferring response: " +
- response.getStatusLine());
-}
-]]></programlisting>
- <para>
- However, please note that the current request and the current response may not
- necessarily represent the same message exchange! Non-blocking HTTP connections can
- operate in a full duplex mode. One can process incoming and outgoing messages
- completely independently from one another. This makes non-blocking HTTP connections
- fully pipelining capable, but at same time implies that this is the job of the protocol
- handler to match logically related request and the response messages.
- </para>
- <para>
- Over-simplified process of submitting a request on the client side may look like this:
- </para>
- <programlisting><![CDATA[
-NHttpClientConnection conn = <...>
-// Obtain execution context
-HttpContext context = conn.getContext();
-// Obtain processing state
-Object state = context.getAttribute("state");
-// Generate a request based on the state information
-HttpRequest request = new BasicHttpRequest("GET", "/");
-
-conn.submitRequest(request);
-System.out.println(conn.isRequestSubmitted());
-]]></programlisting>
- <para>
- Over-simplified process of submitting a response on the server side may look like this:
- </para>
- <programlisting><![CDATA[
-NHttpServerConnection conn = <...>
-// Obtain execution context
-HttpContext context = conn.getContext();
-// Obtain processing state
-Object state = context.getAttribute("state");
-
-// Generate a response based on the state information
-HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
- HttpStatus.SC_OK, "OK");
-BasicHttpEntity entity = new BasicHttpEntity();
-entity.setContentType("text/plain");
-entity.setChunked(true);
-response.setEntity(entity);
-
-conn.submitResponse(response);
-System.out.println(conn.isResponseSubmitted());
-]]></programlisting>
- <para>
- Please note that one should rarely need to transmit messages using these low level
- methods and should use appropriate higher level HTTP service implementations instead.
- </para>
- </section>
- <section>
- <title>HTTP I/O control</title>
- <para>
- All non-blocking HTTP connections classes implement <interfacename>IOControl
- </interfacename> interface, which represents a subset of connection functionality for
- controlling interest in I/O even notifications. <interfacename>IOControl
- </interfacename> instances are expected to be fully thread-safe. Therefore
- <interfacename>IOControl</interfacename> can be used to request / suspend I/O event
- notifications from any thread.
- </para>
- <para>
- One must take special precautions when interacting with non-blocking connections.
- <interfacename>HttpRequest</interfacename> and <interfacename>HttpResponse
- </interfacename>are not thread-safe. It is generally advisable that all input / output
- operations on a non-blocking connection are executed from the I/O event dispatch
- thread.
- </para>
- <para>
- The following pattern is recommended:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- Use <interfacename>IOControl</interfacename> interface to pass control over
- connection's I/O events to another thread / session.
- </para>
- </listitem>
- <listitem>
- <para>
- If input / output operations need be executed on that particular connection,
- store all the required information (state) in the connection context and
- request the appropriate I/O operation by calling <methodname>
- IOControl#requestInput()</methodname> or <methodname>IOControl#requestOutput()
- </methodname> method.
- </para>
- </listitem>
- <listitem>
- <para>
- Execute the required operations from the event method on the dispatch thread
- using information stored in connection context.
- </para>
- </listitem>
- </itemizedlist>
- <para>
- Please note all operations that take place in the event methods should not block for
- too long, because while the dispatch thread remains blocked in one session, it is
- unable to process events for all other sessions. I/O operations with the underlying
- channel of the session are not a problem as they are guaranteed to be non-blocking.
- </para>
- </section>
- <section>
- <title>Non-blocking content transfer</title>
- <para>
- The process of content transfer for non-blocking connections works completely
- differently compared to that of blocking connections, as non-blocking connections need
- to accommodate to the asynchronous nature of the NIO model. The main distinction
- between two types of connections is inability to use the usual, but inherently blocking
- <classname>java.io.InputStream</classname> and <classname>java.io.OutputStream
- </classname> classes to represent streams of inbound and outbound content. HttpCore NIO
- provides <interfacename>ContentEncoder</interfacename> and <interfacename>
- ContentDecoder</interfacename> interfaces to handle the process of asynchronous content
- transfer. Non-blocking HTTP connections will instantiate the appropriate implementation
- of a content codec based on properties of the entity enclosed with the message.
- </para>
- <para>
- Non-blocking HTTP connections will fire input events until the content entity is fully
- transferred.
- </para>
- <programlisting><![CDATA[
-ContentDecoder decoder = <...>
-//Read data in
-ByteBuffer dst = ByteBuffer.allocate(2048);
-decoder.read(dst);
-// Decode will be marked as complete when
-// the content entity is fully transferred
-if (decoder.isCompleted()) {
- // Done
-}
-]]></programlisting>
- <para>
- Non-blocking HTTP connections will fire output events until the content entity is
- marked as fully transferred.
- </para>
- <programlisting><![CDATA[
-ContentEncoder encoder = <...>
-// Prepare output data
-ByteBuffer src = ByteBuffer.allocate(2048);
-// Write data out
-encoder.write(src);
-// Mark content entity as fully transferred when done
-encoder.complete();
-]]></programlisting>
- <para>
- Please note, one still has to provide an HttpEntity instance when submitting an entity
- enclosing message to the non-blocking HTTP connection. Properties of that entity will
- be used to initialize an <interfacename>ContentEncoder</interfacename> instance to be
- used for transferring entity content. Non-blocking HTTP connections, however, ignore
- inherently blocking <methodname>HttpEntity#getContent()</methodname> and <methodname>
- HttpEntity#writeTo()</methodname> methods of the enclosed entities.
- </para>
- <programlisting><![CDATA[
-NHttpServerConnection conn = <...>
-
-HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
- HttpStatus.SC_OK, "OK");
-BasicHttpEntity entity = new BasicHttpEntity();
-entity.setContentType("text/plain");
-entity.setChunked(true);
-entity.setContent(null);
-response.setEntity(entity);
-
-conn.submitResponse(response);
-]]></programlisting>
- <para>
- Likewise, incoming entity enclosing message will have an <interfacename>HttpEntity
- </interfacename> instance associated with them, but an attempt to call <methodname>
- HttpEntity#getContent()</methodname> or <methodname>HttpEntity#writeTo()</methodname>
- methods will cause an <classname>java.lang.IllegalStateException</classname>. The
- <interfacename>HttpEntity</interfacename> instance can be used to determine properties
- of the incoming entity such as content length.
- </para>
- <programlisting><![CDATA[
-NHttpClientConnection conn = <...>
-
-HttpResponse response = conn.getHttpResponse();
-HttpEntity entity = response.getEntity();
-if (entity != null) {
- System.out.println(entity.getContentType());
- System.out.println(entity.getContentLength());
- System.out.println(entity.isChunked());
-}
-]]></programlisting>
- </section>
- <section>
- <title>Supported non-blocking content transfer mechanisms</title>
- <para>
- Default implementations of the non-blocking HTTP connection interfaces support three
- content transfer mechanisms defined by the HTTP/1.1 specification:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><literal>Content-Length</literal> delimited:</title>
- <para>
- The end of the content entity is determined by the value of the
- <literal>Content-Length</literal> header. Maximum entity length:
- <methodname>Long#MAX_VALUE</methodname>.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title>Identity coding:</title>
- <para>
- The end of the content entity is demarcated by closing the underlying
- connection (end of stream condition). For obvious reasons the identity encoding
- can only be used on the server side. Max entity length: unlimited.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title>Chunk coding:</title>
- <para>
- The content is sent in small chunks. Max entity length: unlimited.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- <para>
- The appropriate content codec will be created automatically depending on properties of
- the entity enclosed with the message.
- </para>
- </section>
- <section>
- <title>Direct channel I/O</title>
- <para>
- Content codes are optimized to read data directly from or write data directly to the
- underlying I/O session's channel, whenever possible avoiding intermediate buffering in
- a session buffer. Moreover, those codecs that do not perform any content transformation
- (<literal>Content-Length</literal> delimited and identity codecs, for example) can
- leverage NIO <classname>java.nio.FileChannel</classname> methods for significantly
- improved performance of file transfer operations both inbound and outbound.
- </para>
- <para>
- If the actual content decoder implements <interfacename>FileContentDecoder
- </interfacename> one can make use of its methods to read incoming content directly to a
- file bypassing an intermediate <classname>java.nio.ByteBuffer</classname>.
- </para>
- <programlisting><![CDATA[
-ContentDecoder decoder = <...>
-//Prepare file channel
-FileChannel dst;
-//Make use of direct file I/O if possible
-if (decoder instanceof FileContentDecoder) {
- long Bytesread = ((FileContentDecoder) decoder)
- .transfer(dst, 0, 2048);
- // Decode will be marked as complete when
- // the content entity is fully transmitted
- if (decoder.isCompleted()) {
- // Done
- }
-}
-]]></programlisting>
- <para>
- If the actual content encoder implements <interfacename>FileContentEncoder
- </interfacename> one can make use of its methods to write outgoing content directly
- from a file bypassing an intermediate <classname>java.nio.ByteBuffer</classname>.
- </para>
- <programlisting><![CDATA[
-ContentEncoder encoder = <...>
-// Prepare file channel
-FileChannel src;
-// Make use of direct file I/O if possible
-if (encoder instanceof FileContentEncoder) {
- // Write data out
- long bytesWritten = ((FileContentEncoder) encoder)
- .transfer(src, 0, 2048);
- // Mark content entity as fully transferred when done
- encoder.complete();
-}
-]]></programlisting>
- </section>
- </section>
- <section>
- <title>HTTP I/O event dispatchers</title>
- <para>
- HTTP I/O event dispatchers serve to convert generic I/O events triggered by an I/O reactor
- to HTTP protocol specific events. They rely on <interfacename>NHttpClientEventHandler
- </interfacename> and <interfacename>NHttpServerEventHandler</interfacename> interfaces to
- propagate HTTP protocol events to a HTTP protocol handler.
- </para>
- <para>
- Server side HTTP I/O events as defined by the <interfacename>NHttpServerEventHandler
- </interfacename> interface:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>connected</methodname>:</title>
- <para>
- Triggered when a new incoming connection has been created.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>requestReceived</methodname>:</title>
- <para>
- Triggered when a new HTTP request is received. The connection passed as a parameter
- to this method is guaranteed to return a valid HTTP request object. If the request
- received encloses a request entity this method will be followed a series of
- <methodname>inputReady</methodname> events to transfer the request content.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>inputReady</methodname>:</title>
- <para>
- Triggered when the underlying channel is ready for reading a new portion of
- the request entity through the corresponding content decoder. If the content
- consumer is unable to process the incoming content, input event notifications can
- temporarily suspended using <interfacename>IOControl</interfacename> interface
- (super interface of <interfacename>NHttpServerConnection</interfacename>).
- </para>
- <para>
- Please note that the <interfacename>NHttpServerConnection</interfacename> and
- <interfacename>ContentDecoder</interfacename> objects are not thread-safe and
- should only be used within the context of this method call. The <interfacename>
- IOControl</interfacename> object can be shared and used on other thread to resume
- input event notifications when the handler is capable of processing more content.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>responseReady</methodname>:</title>
- <para>
- Triggered when the connection is ready to accept new HTTP response. The protocol
- handler does not have to submit a response if it is not ready.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>outputReady</methodname>:</title>
- <para>
- Triggered when the underlying channel is ready for writing a next portion of the
- response entity through the corresponding content encoder. If the content producer
- is unable to generate the outgoing content, output event notifications can be
- temporarily suspended using <interfacename>IOControl</interfacename> interface
- (super interface of <interfacename>NHttpServerConnection</interfacename>).
- </para>
- <para>
- Please note that the <interfacename>NHttpServerConnection</interfacename> and
- <interfacename>ContentEncoder</interfacename> objects are not thread-safe and
- should only be used within the context of this method call. The <interfacename>
- IOControl</interfacename> object can be shared and used on other thread to resume
- output event notifications when more content is made available.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>exception</methodname>:</title>
- <para>
- Triggered when an I/O error occurrs while reading from or writing to the underlying
- channel or when an HTTP protocol violation occurs while receiving an HTTP request.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>timeout</methodname>:</title>
- <para>
- Triggered when no input is detected on this connection over the maximum period of
- inactivity.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>closed</methodname>:</title>
- <para>
- Triggered when the connection has been closed.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- <para>
- Client side HTTP I/O events as defined by the <interfacename>NHttpClientEventHandler
- </interfacename> interface:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>connected</methodname>:</title>
- <para>
- Triggered when a new outgoing connection has been created. The attachment object
- passed as a parameter to this event is an arbitrary object that was attached to
- the session request.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>requestReady</methodname>:</title>
- <para>
- Triggered when the connection is ready to accept new HTTP request. The protocol
- handler does not have to submit a request if it is not ready.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>outputReady</methodname>:</title>
- <para>
- Triggered when the underlying channel is ready for writing a next portion of the
- request entity through the corresponding content encoder. If the content producer
- is unable to generate the outgoing content, output event notifications can be
- temporarily suspended using <interfacename>IOControl</interfacename> interface
- (super interface of <interfacename>NHttpClientConnection</interfacename>).
- </para>
- <para>
- Please note that the <interfacename>NHttpClientConnection</interfacename> and
- <interfacename>ContentEncoder</interfacename> objects are not thread-safe and
- should only be used within the context of this method call. The <interfacename>
- IOControl</interfacename> object can be shared and used on other thread to resume
- output event notifications when more content is made available.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>responseReceived</methodname>:</title>
- <para>
- Triggered when an HTTP response is received. The connection passed as a parameter to
- this method is guaranteed to return a valid HTTP response object. If the response
- received encloses a response entity this method will be followed a series of
- <methodname>inputReady</methodname> events to transfer the response content.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>inputReady</methodname>:</title>
- <para>
- Triggered when the underlying channel is ready for reading a new portion of the
- response entity through the corresponding content decoder. If the content consumer
- is unable to process the incoming content, input event notifications can be
- temporarily suspended using <interfacename>IOControl</interfacename> interface
- (super interface of <interfacename>NHttpClientConnection</interfacename>).
- </para>
- <para>
- Please note that the <interfacename>NHttpClientConnection</interfacename> and
- <interfacename>ContentDecoder</interfacename> objects are not thread-safe and
- should only be used within the context of this method call. The <interfacename>
- IOControl</interfacename> object can be shared and used on other thread to resume
- input event notifications when the handler is capable of processing more content.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>exception</methodname>:</title>
- <para>
- Triggered when an I/O error occurs while reading from or writing to the underlying
- channel or when an HTTP protocol violation occurs while receiving an HTTP response.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>timeout</methodname>:</title>
- <para>
- Triggered when no input is detected on this connection over the maximum period of
- inactivity.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>closed</methodname>:</title>
- <para>
- Triggered when the connection has been closed.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- </section>
- <section>
- <title>Non-blocking HTTP content producers</title>
- <para>
- As discussed previously the process of content transfer for non-blocking connections works
- completely differently compared to that for blocking connections. For obvious reasons
- classic I/O abstraction based on inherently blocking <classname>java.io.InputStream
- </classname> and <classname>java.io.OutputStream</classname> classes is not well suited
- for asynchronous data transfer. In order to avoid inefficient and potentially blocking
- I/O operation redirection through <methodname>java.nio.channels.Channles#newChannel
- </methodname> non-blocking HTTP entities are expected to implement NIO specific extension
- interface <interfacename>HttpAsyncContentProducer</interfacename>.
- </para>
- <para>
- The <interfacename>HttpAsyncContentProducer</interfacename> interface defines several
- additional method for efficient streaming of content to a non-blocking HTTP connection:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>produceContent</methodname>:</title>
- <para>
- Invoked to write out a chunk of content to the <interfacename>ContentEncoder
- </interfacename>. The <interfacename>IOControl</interfacename> interface can be
- used to suspend output events if the entity is temporarily unable to produce more
- content. When all content is finished, the producer MUST call
- <methodname>ContentEncoder#complete()</methodname>. Failure to do so may cause
- the entity to be incorrectly delimited.
- </para>
- <para>
- Please note that the <interfacename>ContentEncoder</interfacename> object is
- not thread-safe and should only be used within the context of this method call.
- The <interfacename>IOControl</interfacename> object can be shared and used on other
- thread resume output event notifications when more content is made available.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>isRepeatable</methodname>:</title>
- <para>
- Determines whether or not this producer is capable of producing its content more
- than once. Repeatable content producers are expected to be able to recreate
- their content even after having been closed.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>close</methodname>:</title>
- <para>
- Closes the producer and releases all resources currently allocated by it.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- <section>
- <title>Creating non-blocking entities</title>
- <para>
- Several HTTP entity implementations included in HttpCore NIO support
- <interfacename>HttpAsyncContentProducer</interfacename> interface:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <link linkend="bytearray-n-entity">
- <classname>NByteArrayEntity</classname>
- </link>
- </para>
- </listitem>
- <listitem>
- <para>
- <link linkend="string-n-entity">
- <classname>NStringEntity</classname>
- </link>
- </para>
- </listitem>
- <listitem>
- <para>
- <link linkend="file-n-entity">
- <classname>NFileEntity</classname>
- </link>
- </para>
- </listitem>
- </itemizedlist>
- <section id="bytearray-n-entity">
- <title><classname>NByteArrayEntity</classname></title>
- <para>
- This is a simple self-contained repeatable entity, which receives its content from
- a given byte array. This byte array is supplied to the constructor.
- </para>
- <programlisting><![CDATA[
-NByteArrayEntity entity = new NByteArrayEntity(new byte[] {1, 2, 3});
-]]></programlisting>
- </section>
- <section id="string-n-entity">
- <title><classname>NStringEntity</classname></title>
- <para>
- This is a simple, self-contained, repeatable entity that retrieves its data from a
- <classname>java.lang.String</classname> object. It has 2 constructors, one simply
- constructs with a given string where the other also takes a character encoding for
- the data in the <classname>java.lang.String</classname>.
- </para>
- <programlisting><![CDATA[
-NStringEntity myEntity = new NStringEntity("important message",
- Consts.UTF_8);
- ]]></programlisting>
- </section>
- <section id="file-n-entity">
- <title><classname>NFileEntity</classname></title>
- <para>
- This entity reads its content body from a file. This class is mostly used to stream
- large files of different types, so one needs to supply the content type of the file
- to make sure the content can be correctly recognized and processed by the
- recipient.
- </para>
- <programlisting><![CDATA[
-File staticFile = new File("/path/to/myapp.jar");
-NFileEntity entity = new NFileEntity(staticFile,
- ContentType.create("application/java-archive", null));
- ]]></programlisting>
- <para>
- The <classname>NHttpEntity</classname> will make use of the direct channel I/O
- whenever possible, provided the content encoder is capable of transferring data
- directly from a file to the socket of the underlying connection.
- </para>
- </section>
- </section>
- </section>
- <section>
- <title>Non-blocking HTTP protocol handlers</title>
- <section>
- <title>Asynchronous HTTP service</title>
- <para>
- <classname>HttpAsyncService</classname> is a fully asynchronous HTTP server side
- protocol handler based on the non-blocking (NIO) I/O model. <classname>
- HttpAsyncService</classname> translates individual events fired through the
- <interfacename>NHttpServerEventHandler</interfacename> interface into logically
- related HTTP message exchanges.
- </para>
- <para>
- Upon receiving an incoming request the <classname>HttpAsyncService</classname>
- verifies the message for compliance with the server expectations using <interfacename>
- HttpAsyncExpectationVerifier</interfacename>, if provided, and then <interfacename>
- HttpAsyncRequestHandlerResolver</interfacename> is used to resolve the request URI to
- a particular <interfacename>HttpAsyncRequestHandler</interfacename> intended to handle
- the request with the given URI. The protocol handler uses the selected <interfacename>
- HttpAsyncRequestHandler</interfacename> instance to process the incoming request and
- to generate an outgoing response.
- </para>
- <para>
- <classname>HttpAsyncService</classname> relies on <interfacename>HttpProcessor
- </interfacename> to generate mandatory protocol headers for all outgoing messages
- and apply common, cross-cutting message transformations to all incoming and outgoing
- messages, whereas individual HTTP request handlers are expected to implement
- application specific content generation and processing.
- </para>
- <programlisting><![CDATA[
-HttpProcessor httpproc = HttpProcessorBuilder.create()
- .add(new ResponseDate())
- .add(new ResponseServer("MyServer-HTTP/1.1"))
- .add(new ResponseContent())
- .add(new ResponseConnControl())
- .build();
-HttpAsyncService protocolHandler = new HttpAsyncService(httpproc, null);
-IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(
- protocolHandler,
- new DefaultNHttpServerConnectionFactory(ConnectionConfig.DEFAULT));
-ListeningIOReactor ioreactor = new DefaultListeningIOReactor();
-ioreactor.execute(ioEventDispatch);
-]]></programlisting>
- <section>
- <title>Non-blocking HTTP request handlers</title>
- <para>
- <interfacename>HttpAsyncRequestHandler</interfacename> represents a routine for
- asynchronous processing of a specific group of non-blocking HTTP requests.
- Protocol handlers are designed to take care of protocol specific aspects, whereas
- individual request handlers are expected to take care of application specific HTTP
- processing. The main purpose of a request handler is to generate a response object
- with a content entity to be sent back to the client in response to the given
- request.
- </para>
- <programlisting><![CDATA[
-HttpAsyncRequestHandler<HttpRequest> rh = new HttpAsyncRequestHandler<HttpRequest>() {
-
- public HttpAsyncRequestConsumer<HttpRequest> processRequest(
- final HttpRequest request,
- final HttpContext context) {
- // Buffer request content in memory for simplicity
- return new BasicAsyncRequestConsumer();
- }
-
- public void handle(
- final HttpRequest request,
- final HttpAsyncExchange httpexchange,
- final HttpContext context) throws HttpException, IOException {
- HttpResponse response = httpexchange.getResponse();
- response.setStatusCode(HttpStatus.SC_OK);
- NFileEntity body = new NFileEntity(new File("static.html"),
- ContentType.create("text/html", Consts.UTF_8));
- response.setEntity(body);
- httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
- }
-
-};
-]]></programlisting>
- <para>
- Request handlers must be implemented in a thread-safe manner. Similarly to
- servlets, request handlers should not use instance variables unless access to those
- variables are synchronized.
- </para>
- </section>
- <section>
- <title>Asynchronous HTTP exchange</title>
- <para>
- The most fundamental difference of the non-blocking request handlers compared to
- their blocking counterparts is ability to defer transmission of the HTTP response
- back to the client without blocking the I/O thread by delegating the process of
- handling the HTTP request to a worker thread or another service. The instance of
- <interfacename>HttpAsyncExchange</interfacename> passed as a parameter to the
- <methodname>HttpAsyncRequestHandler#handle</methodname> method to submit
- a response as at a later point once response content becomes available.
- </para>
- <para>
- The <interfacename>HttpAsyncExchange</interfacename> interface can be interacted
- with using the following methods:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>getRequest</methodname>:</title>
- <para>
- Returns the received HTTP request message.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>getResponse</methodname>:</title>
- <para>
- Returns the default HTTP response message that can submitted once ready.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>submitResponse</methodname>:</title>
- <para>
- Submits an HTTP response and completed the message exchange.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>isCompleted</methodname>:</title>
- <para>
- Determines whether or not the message exchange has been completed.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>setCallback</methodname>:</title>
- <para>
- Sets <interfacename>Cancellable</interfacename> callback to be invoked
- in case the underlying connection times out or gets terminated prematurely
- by the client. This callback can be used to cancel a long running response
- generating process if a response is no longer needed.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>setTimeout</methodname>:</title>
- <para>
- Sets timeout for this message exchange.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>getTimeout</methodname>:</title>
- <para>
- Returns timeout for this message exchange.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- <programlisting><![CDATA[
-HttpAsyncRequestHandler<HttpRequest> rh = new HttpAsyncRequestHandler<HttpRequest>() {
-
- public HttpAsyncRequestConsumer<HttpRequest> processRequest(
- final HttpRequest request,
- final HttpContext context) {
- // Buffer request content in memory for simplicity
- return new BasicAsyncRequestConsumer();
- }
-
- public void handle(
- final HttpRequest request,
- final HttpAsyncExchange httpexchange,
- final HttpContext context) throws HttpException, IOException {
-
- new Thread() {
-
- @Override
- public void run() {
- try {
- Thread.sleep(10);
- }
- catch(InterruptedException ie) {}
- HttpResponse response = httpexchange.getResponse();
- response.setStatusCode(HttpStatus.SC_OK);
- NFileEntity body = new NFileEntity(new File("static.html"),
- ContentType.create("text/html", Consts.UTF_8));
- response.setEntity(body);
- httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
- }
- }.start();
-
- }
-
-};
-]]></programlisting>
- <para>
- Please note <interfacename>HttpResponse</interfacename> instances are not
- thread-safe and may not be modified concurrently. Non-blocking request handlers
- must ensure HTTP response cannot be accessed by more than one thread at a time.
- </para>
- </section>
- <section>
- <title>Asynchronous HTTP request consumer</title>
- <para>
- <interfacename>HttpAsyncRequestConsumer</interfacename> facilitates the process of
- asynchronous processing of HTTP requests. It is a callback interface used by
- <interfacename>HttpAsyncRequestHandler</interfacename>s to process an incoming
- HTTP request message and to stream its content from a non-blocking server side
- HTTP connection.
- </para>
- <para>
- HTTP I/O events and methods as defined by the <interfacename>
- HttpAsyncRequestConsumer</interfacename> interface:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>requestReceived</methodname>:</title>
- <para>
- Invoked when a HTTP request message is received.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>consumeContent</methodname>:</title>
- <para>
- Invoked to process a chunk of content from the <interfacename>
- ContentDecoder</interfacename>. The <interfacename>IOControl
- </interfacename> interface can be used to suspend input events if
- the consumer is temporarily unable to consume more content.
- </para>
- <para>
- The consumer can use the <methodname>ContentDecoder#isCompleted()
- </methodname> method to find out whether or not the message content
- has been fully consumed.
- </para>
- <para>
- Please note that the <interfacename>ContentDecoder</interfacename> object
- is not thread-safe and should only be used within the context of this
- method call. The <interfacename>IOControl</interfacename> object can be
- shared and used on other thread to resume input event notifications
- when the consumer is capable of processing more content.
- </para>
- <para>
- This event is invoked only if the incoming request message has
- a content entity enclosed in it.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>requestCompleted</methodname>:</title>
- <para>
- Invoked to signal that the request has been fully processed.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>failed</methodname>:</title>
- <para>
- Invoked to signal that the request processing terminated abnormally.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>getException</methodname>:</title>
- <para>
- Returns an exception in case of an abnormal termination. This method
- returns <code>null</code> if the request execution is still ongoing or if
- it completed successfully.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>getResult</methodname>:</title>
- <para>
- Returns a result of the request execution, when available. This method
- returns <code>null</code> if the request execution is still ongoing.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>isDone</methodname>:</title>
- <para>
- Determines whether or not the request execution completed. If the
- request processing terminated normally <methodname>getResult()</methodname>
- can be used to obtain the result. If the request processing terminated
- abnormally <methodname>getException()</methodname> can be used to obtain
- the cause.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>close</methodname>:</title>
- <para>
- Closes the consumer and releases all resources currently allocated by it.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- <para>
- <interfacename>HttpAsyncRequestConsumer</interfacename> implementations are
- expected to be thread-safe.
- </para>
- <para>
- <classname>BasicAsyncRequestConsumer</classname> is a very basic implementation
- of the <interfacename>HttpAsyncRequestConsumer</interfacename> interface shipped
- with the library. Please note that this consumer buffers request content in memory and
- therefore should be used for relatively small request messages.
- </para>
- </section>
- <section>
- <title>Asynchronous HTTP response producer</title>
- <para>
- <interfacename>HttpAsyncResponseProducer</interfacename> facilitates the process of
- asynchronous generation of HTTP responses. It is a callback interface used by
- <interfacename>HttpAsyncRequestHandler</interfacename>s to generate an HTTP response
- message and to stream its content to a non-blocking server side HTTP connection.
- </para>
- <para>
- HTTP I/O events and methods as defined by the
- <interfacename>HttpAsyncResponseProducer</interfacename> interface:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>generateResponse</methodname>:</title>
- <para>
- Invoked to generate a HTTP response message header.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>produceContent</methodname>:</title>
- <para>
- Invoked to write out a chunk of content to the <interfacename>
- ContentEncoder</interfacename>. The <interfacename>IOControl
- </interfacename> interface can be used to suspend output events if
- the producer is temporarily unable to produce more content.
- </para>
- <para>
- When all content is finished, the producer MUST call <methodname>
- ContentEncoder#complete()</methodname>. Failure to do so may cause
- the entity to be incorrectly delimited.
- </para>
- <para>
- Please note that the <interfacename>ContentEncoder</interfacename> object
- is not thread-safe and should only be used within the context of this
- method call. The <interfacename>IOControl</interfacename> object can be
- shared and used on other thread resume output event notifications when
- more content is made available.
- </para>
- <para>
- This event is invoked only for if the outgoing response message has
- a content entity enclosed in it, that is <methodname>
- HttpResponse#getEntity()</methodname> returns <code>null</code>.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>responseCompleted</methodname>:</title>
- <para>
- Invoked to signal that the response has been fully written out.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>failed</methodname>:</title>
- <para>
- Invoked to signal that the response processing terminated abnormally.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>close</methodname>:</title>
- <para>
- Closes the producer and releases all resources currently allocated by it.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- <para>
- <interfacename>HttpAsyncResponseProducer</interfacename> implementations are
- expected to be thread-safe.
- </para>
- <para>
- <classname>BasicAsyncResponseProducer</classname> is a basic implementation
- of the <interfacename>HttpAsyncResponseProducer</interfacename> interface shipped
- with the library. The producer can make use of the <interfacename>
- HttpAsyncContentProducer</interfacename> interface to efficiently stream out
- message content to a non-blocking HTTP connection, if it is implemented by the
- <interfacename>HttpEntity</interfacename> enclosed in the response.
- </para>
- </section>
- <section>
- <title>Non-blocking request handler resolver</title>
- <para>
- The management of non-blocking HTTP request handlers is quite similar to that of
- blocking HTTP request handlers. Usually an instance of <interfacename>
- HttpAsyncRequestHandlerResolver</interfacename> is used to maintain a registry of
- request handlers and to matches a request URI to a particular request handler.
- HttpCore includes only a very simple implementation of the request handler resolver
- based on a trivial pattern matching algorithm: <classname>
- HttpAsyncRequestHandlerRegistry</classname> supports only three formats:
- <literal>*</literal>, <literal><uri>*</literal> and
- <literal>*<uri></literal>.
- </para>
- <programlisting><![CDATA[
-HttpAsyncRequestHandler<?> myRequestHandler1 = <...>
-HttpAsyncRequestHandler<?> myRequestHandler2 = <...>
-HttpAsyncRequestHandler<?> myRequestHandler3 = <...>
-UriHttpAsyncRequestHandlerMapper handlerReqistry =
- new UriHttpAsyncRequestHandlerMapper();
-handlerReqistry.register("/service/*", myRequestHandler1);
-handlerReqistry.register("*.do", myRequestHandler2);
-handlerReqistry.register("*", myRequestHandler3);
-]]></programlisting>
- <para>
- Users are encouraged to provide more sophisticated implementations of
- <interfacename>HttpAsyncRequestHandlerResolver</interfacename>, for instance, based
- on regular expressions.
- </para>
- </section>
- </section>
- <section>
- <title>Asynchronous HTTP request executor</title>
- <para>
- <classname>HttpAsyncRequestExecutor</classname> is a fully asynchronous client side
- HTTP protocol handler based on the NIO (non-blocking) I/O model. <classname>
- HttpAsyncRequestExecutor</classname> translates individual events fired through the
- <interfacename>NHttpClientEventHandler</interfacename> interface into logically
- related HTTP message exchanges.
- </para>
- <para>
- <classname>HttpAsyncRequestExecutor</classname> relies on <interfacename>
- HttpAsyncRequestExecutionHandler</interfacename> to implement application specific
- content generation and processing and to handle logically related series of HTTP
- request / response exchanges, which may also span across multiple connections.
- <interfacename>HttpProcessor</interfacename> provided by the <interfacename>
- HttpAsyncRequestExecutionHandler</interfacename> instance will be used to generate
- mandatory protocol headers for all outgoing messages and apply common, cross-cutting
- message transformations to all incoming and outgoing messages. The caller is expected
- to pass an instance of <interfacename>HttpAsyncRequestExecutionHandler</interfacename>
- to be used for the next series of HTTP message exchanges through the connection
- context using <methodname>HttpAsyncRequestExecutor#HTTP_HANDLER</methodname> attribute.
- HTTP exchange sequence is considered complete when the <methodname>
- HttpAsyncRequestExecutionHandler#isDone()</methodname> method returns <code>true</code>.
- </para>
- <programlisting><![CDATA[
-HttpAsyncRequestExecutor ph = new HttpAsyncRequestExecutor();
-IOEventDispatch ioEventDispatch = new DefaultHttpClientIODispatch(ph,
- new DefaultNHttpClientConnectionFactory(ConnectionConfig.DEFAULT));
-ConnectingIOReactor ioreactor = new DefaultConnectingIOReactor();
-ioreactor.execute(ioEventDispatch);
-]]></programlisting>
- <para>
- The <classname>HttpAsyncRequester</classname> utility class can be used to abstract
- away low level details of <interfacename>HttpAsyncRequestExecutionHandler
- </interfacename> management. Please note <classname>HttpAsyncRequester</classname>
- supports single HTTP request / response exchanges only. It does not support HTTP
- authentication and does not handle redirects automatically.
- </para>
- <programlisting><![CDATA[
-HttpProcessor httpproc = HttpProcessorBuilder.create()
- .add(new RequestContent())
- .add(new RequestTargetHost())
- .add(new RequestConnControl())
- .add(new RequestUserAgent("MyAgent-HTTP/1.1"))
- .add(new RequestExpectContinue(true))
- .build();
-HttpAsyncRequester requester = new HttpAsyncRequester(httpproc);
-NHttpClientConnection conn = <...>
-Future<HttpResponse> future = requester.execute(
- new BasicAsyncRequestProducer(
- new HttpHost("localhost"),
- new BasicHttpRequest("GET", "/")),
- new BasicAsyncResponseConsumer(),
- conn);
-HttpResponse response = future.get();
-]]></programlisting>
- <section>
- <title>Asynchronous HTTP request producer</title>
- <para>
- <interfacename>HttpAsyncRequestProducer</interfacename> facilitates the process of
- asynchronous generation of HTTP requests. It is a callback interface whose methods
- get invoked to generate an HTTP request message and to stream message content to
- a non-blocking client side HTTP connection.
- </para>
- <para>
- Repeatable request producers capable of generating the same request message more
- than once can be reset to their initial state by calling the <methodname>
- resetRequest()</methodname> method, at which point request producers are expected
- to release currently allocated resources that are no longer needed or re-acquire
- resources needed to repeat the process.
- </para>
- <para>
- HTTP I/O events and methods as defined by the
- <interfacename>HttpAsyncRequestProducer</interfacename> interface:
- </para>
- <itemizedlist>
- <listitem>
- <formalpara>
- <title><methodname>getTarget</methodname>:</title>
- <para>
- Invoked to obtain the request target host.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>generateRequest</methodname>:</title>
- <para>
- Invoked to generate a HTTP request message header. The message is expected
- to implement the <interfacename>HttpEntityEnclosingRequest</interfacename>
- interface if it is to enclose a content entity.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>produceContent</methodname>:</title>
- <para>
- Invoked to write out a chunk of content to the <interfacename>
- ContentEncoder</interfacename>. The <interfacename>IOControl
- </interfacename> interface can be used to suspend output events if
- the producer is temporarily unable to produce more content.
- </para>
- <para>
- When all content is finished, the producer MUST call <methodname>
- ContentEncoder#complete()</methodname>. Failure to do so may cause
- the entity to be incorrectly delimited
- </para>
- <para>
- Please note that the <interfacename>ContentEncoder</interfacename> object
- is not thread-safe and should only be used within the context of this
- method call. The <interfacename>IOControl</interfacename> object can be
- shared and used on other thread resume output event notifications when
- more content is made available.
- </para>
- <para>
- This event is invoked only for if the outgoing request message has
- a content entity enclosed in it, that is <methodname>
- HttpEntityEnclosingRequest#getEntity()</methodname> returns <code>null
- </code>.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>requestCompleted</methodname>:</title>
- <para>
- Invoked to signal that the request has been fully written out.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>failed</methodname>:</title>
- <para>
- Invoked to signal that the request processing terminated abnormally.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>resetRequest</methodname>:</title>
- <para>
- Invoked to reset the producer to its initial state. Repeatable request
- producers are expected to release currently allocated resources that are
- no longer needed or re-acquire resources needed to repeat the process.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
- <title><methodname>close</methodname>:</title>
- <para>
- Closes the producer and releases all resources currently allocated by it.
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- <para>
- <interfacename>HttpAsyncRequestProducer</interfacename> implementations are
- expected to be thread-safe.
- </para>
- <para>
- <classname>BasicAsyncRequestProducer</classname> is a basic implementation
- of the <interfacename>HttpAsyncRequestProducer</interfacename> interface shipped
- with the library. The producer can mak
<TRUNCATED>