blob: 76c38baf77b75035caa09ed3653fccdcdd21b024 [file] [log] [blame]
<?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="advanced">
<title>Advanced topics</title>
<section>
<title>HTTP message parsing and formatting framework</title>
<para>
HTTP message processing framework is designed to be expressive and flexible while remaining
memory efficient and fast. HttpCore HTTP message processing code achieves near zero
intermediate garbage and near zero-copy buffering for its parsing and formatting
operations. The same HTTP message parsing and formatting API and implementations are used
by both the blocking and non-blocking transport implementations, which helps ensure a
consistent behavior of HTTP services regardless of the I/O model.
</para>
<section>
<title>HTTP line parsing and formatting</title>
<para>
HttpCore utilizes a number of low level components for all its line parsing and
formatting methods.
</para>
<para>
<classname>CharArrayBuffer</classname> represents a sequence of characters, usually a
single line in an HTTP message stream such as a request line, a status line or a
header. Internally <classname>CharArrayBuffer</classname> is backed by an array of
chars, which can be expanded to accommodate more input if needed. <classname>
CharArrayBuffer</classname> also provides a number of utility methods for manipulating
content of the buffer, storing more data and retrieving subsets of data.
</para>
<programlisting><![CDATA[
CharArrayBuffer buf = new CharArrayBuffer(64);
buf.append("header: data ");
int i = buf.indexOf(':');
String s = buf.substringTrimmed(i + 1, buf.length());
System.out.println(s);
System.out.println(s.length());
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
data
4
]]></programlisting>
<para>
<classname>ParserCursor</classname> represents a context of a parsing operation: the
bounds limiting the scope of the parsing operation and the current position the parsing
operation is expected to start at.
</para>
<programlisting><![CDATA[
CharArrayBuffer buf = new CharArrayBuffer(64);
buf.append("header: data ");
int i = buf.indexOf(':');
ParserCursor cursor = new ParserCursor(0, buf.length());
cursor.updatePos(i + 1);
System.out.println(cursor);
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
[0>7>14]
]]></programlisting>
<para>
<interfacename>LineParser</interfacename> is the interface for parsing lines in the
head section of an HTTP message. There are individual methods for parsing a request
line, a status line, or a header line. The lines to parse are passed in-memory, the
parser does not depend on any specific I/O mechanism.
</para>
<programlisting><![CDATA[
CharArrayBuffer buf = new CharArrayBuffer(64);
buf.append("HTTP/1.1 200");
ParserCursor cursor = new ParserCursor(0, buf.length());
LineParser parser = BasicLineParser.INSTANCE;
ProtocolVersion ver = parser.parseProtocolVersion(buf, cursor);
System.out.println(ver);
System.out.println(buf.substringTrimmed(
cursor.getPos(),
cursor.getUpperBound()));
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
HTTP/1.1
200
]]></programlisting>
<programlisting><![CDATA[
CharArrayBuffer buf = new CharArrayBuffer(64);
buf.append("HTTP/1.1 200 OK");
ParserCursor cursor = new ParserCursor(0, buf.length());
LineParser parser = new BasicLineParser();
StatusLine sl = parser.parseStatusLine(buf, cursor);
System.out.println(sl.getReasonPhrase());
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
OK
]]></programlisting>
<para>
<interfacename>LineFormatter</interfacename> for formatting elements of the head
section of an HTTP message. This is the complement to <interfacename>LineParser
</interfacename>. There are individual methods for formatting a request line, a status
line, or a header line.
</para>
<para>
Please note the formatting does not include the trailing line break sequence
<literal>CR-LF</literal>.
</para>
<programlisting><![CDATA[
CharArrayBuffer buf = new CharArrayBuffer(64);
LineFormatter formatter = new BasicLineFormatter();
formatter.formatRequestLine(buf,
new BasicRequestLine("GET", "/", HttpVersion.HTTP_1_1));
System.out.println(buf.toString());
formatter.formatHeader(buf,
new BasicHeader("Content-Type", "text/plain"));
System.out.println(buf.toString());
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
GET / HTTP/1.1
Content-Type: text/plain
]]></programlisting>
<para>
<interfacename>HeaderValueParser</interfacename> is the interface for parsing header
values into elements.
</para>
<programlisting><![CDATA[
CharArrayBuffer buf = new CharArrayBuffer(64);
HeaderValueParser parser = new BasicHeaderValueParser();
buf.append("name1=value1; param1=p1, " +
"name2 = \"value2\", name3 = value3");
ParserCursor cursor = new ParserCursor(0, buf.length());
System.out.println(parser.parseHeaderElement(buf, cursor));
System.out.println(parser.parseHeaderElement(buf, cursor));
System.out.println(parser.parseHeaderElement(buf, cursor));
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
name1=value1; param1=p1
name2=value2
name3=value3
]]></programlisting>
<para>
<interfacename>HeaderValueFormatter</interfacename> is the interface for formatting
elements of a header value. This is the complement to <interfacename>HeaderValueParser
</interfacename>.
</para>
<programlisting><![CDATA[
CharArrayBuffer buf = new CharArrayBuffer(64);
HeaderValueFormatter formatter = new BasicHeaderValueFormatter();
HeaderElement[] hes = new HeaderElement[] {
new BasicHeaderElement("name1", "value1",
new NameValuePair[] {
new BasicNameValuePair("param1", "p1")} ),
new BasicHeaderElement("name2", "value2"),
new BasicHeaderElement("name3", "value3"),
};
formatter.formatElements(buf, hes, true);
System.out.println(buf.toString());
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
name1="value1"; param1="p1", name2="value2", name3="value3"
]]></programlisting>
</section>
<section>
<title>HTTP message streams and session I/O buffers</title>
<para>
HttpCore provides a number of utility classes for the blocking and non-blocking I/O
models that facilitate the processing of HTTP message streams, simplify handling of
<literal>CR-LF</literal> delimited lines in HTTP messages and manage intermediate data
buffering.
</para>
<para>
HTTP connection implementations usually rely on session input/output buffers for
reading and writing data from and to an HTTP message stream. Session input/output
buffer implementations are I/O model specific and are optimized either for blocking or
non-blocking operations.
</para>
<para>
Blocking HTTP connections use socket bound session buffers to transfer data. Session
buffer interfaces are similar to <classname>java.io.InputStream</classname> /
<classname>java.io.OutputStream</classname> classes, but they also provide methods for
reading and writing <literal>CR-LF</literal> delimited lines.
</para>
<programlisting><![CDATA[
Socket socket1 = <...>
Socket socket2 = <...>
HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
SessionInputBufferImpl inbuffer = new SessionInputBufferImpl(metrics, 8 * 1024);
inbuffer.bind(socket1.getInputStream());
SessionOutputBufferImpl outbuffer = new SessionOutputBufferImpl(metrics, 8 * 1024);
outbuffer.bind(socket2.getOutputStream());
CharArrayBuffer linebuf = new CharArrayBuffer(1024);
inbuffer.readLine(linebuf);
outbuffer.writeLine(linebuf);
]]></programlisting>
<para>
Non-blocking HTTP connections use session buffers optimized for reading and writing
data from and to non-blocking NIO channels. NIO session input/output sessions help deal
with <literal>CR-LF</literal> delimited lines in a non-blocking I/O mode.
</para>
<programlisting><![CDATA[
ReadableByteChannel channel1 = <...>
WritableByteChannel channel2 = <...>
SessionInputBuffer inbuffer = new SessionInputBufferImpl(8 * 1024);
SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(8 * 1024);
CharArrayBuffer linebuf = new CharArrayBuffer(1024);
boolean endOfStream = false;
int bytesRead = inbuffer.fill(channel1);
if (bytesRead == -1) {
endOfStream = true;
}
if (inbuffer.readLine(linebuf, endOfStream)) {
outbuffer.writeLine(linebuf);
}
if (outbuffer.hasData()) {
outbuffer.flush(channel2);
}
]]></programlisting>
</section>
<section>
<title>HTTP message parsers and formatters</title>
<para>
HttpCore also provides coarse-grained facade type interfaces for parsing and
formatting of HTTP messages. Default implementations of those interfaces build upon the
functionality provided by <interfacename>SessionInputBuffer</interfacename> /
<interfacename>SessionOutputBuffer</interfacename> and <interfacename>HttpLineParser
</interfacename>/ <interfacename>HttpLineFormatter</interfacename> implementations.
</para>
<para>
Example of HTTP request parsing / writing for blocking HTTP connections:
</para>
<programlisting><![CDATA[
SessionInputBuffer inbuffer = <...>
SessionOutputBuffer outbuffer = <...>
HttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(
inbuffer);
HttpRequest request = requestParser.parse();
HttpMessageWriter<HttpRequest> requestWriter = new DefaultHttpRequestWriter(
outbuffer);
requestWriter.write(request);
]]></programlisting>
<para>
Example of HTTP response parsing / writing for blocking HTTP connections:
</para>
<programlisting><![CDATA[
SessionInputBuffer inbuffer = <...>
SessionOutputBuffer outbuffer = <...>
HttpMessageParser<HttpResponse> responseParser = new DefaultHttpResponseParser(
inbuffer);
HttpResponse response = responseParser.parse();
HttpMessageWriter<HttpResponse> responseWriter = new DefaultHttpResponseWriter(
outbuffer);
responseWriter.write(response);
]]></programlisting>
<para>
Custom message parsers and writers can be plugged into the message processing pipeline
through a custom connection factory:
</para>
<programlisting><![CDATA[
HttpMessageWriterFactory<HttpResponse> responseWriterFactory =
new HttpMessageWriterFactory<HttpResponse>() {
@Override
public HttpMessageWriter<HttpResponse> create(
SessionOutputBuffer buffer) {
HttpMessageWriter<HttpResponse> customWriter = <...>
return customWriter;
}
};
HttpMessageParserFactory<HttpRequest> requestParserFactory =
new HttpMessageParserFactory<HttpRequest>() {
@Override
public HttpMessageParser<HttpRequest> create(
SessionInputBuffer buffer,
MessageConstraints constraints) {
HttpMessageParser<HttpRequest> customParser = <...>
return customParser;
}
};
HttpConnectionFactory<DefaultBHttpServerConnection> cf =
new DefaultBHttpServerConnectionFactory(
ConnectionConfig.DEFAULT,
requestParserFactory,
responseWriterFactory);
Socket socket = <...>
DefaultBHttpServerConnection conn = cf.createConnection(socket);
]]></programlisting>
<para>
Example of HTTP request parsing / writing for non-blocking HTTP connections:
</para>
<programlisting><![CDATA[
SessionInputBuffer inbuffer = <...>
SessionOutputBuffer outbuffer = <...>
NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(
inbuffer);
HttpRequest request = requestParser.parse();
NHttpMessageWriter<HttpRequest> requestWriter = new DefaultHttpRequestWriter(
outbuffer);
requestWriter.write(request);
]]></programlisting>
<para>
Example of HTTP response parsing / writing for non-blocking HTTP connections:
</para>
<programlisting><![CDATA[
SessionInputBuffer inbuffer = <...>
SessionOutputBuffer outbuffer = <...>
NHttpMessageParser<HttpResponse> responseParser = new DefaultHttpResponseParser(
inbuffer);
HttpResponse response = responseParser.parse();
NHttpMessageWriter responseWriter = new DefaultHttpResponseWriter(
outbuffer);
responseWriter.write(response);
]]></programlisting>
</section>
<para>
Custom non-blocking message parsers and writers can be plugged into the message processing
pipeline through a custom connection factory:
</para>
<programlisting><![CDATA[
NHttpMessageWriterFactory<HttpResponse> responseWriterFactory =
new NHttpMessageWriterFactory<HttpResponse>() {
@Override
public NHttpMessageWriter<HttpResponse> create(SessionOutputBuffer buffer) {
NHttpMessageWriter<HttpResponse> customWriter = <...>
return customWriter;
}
};
NHttpMessageParserFactory<HttpRequest> requestParserFactory =
new NHttpMessageParserFactory<HttpRequest>() {
@Override
public NHttpMessageParser<HttpRequest> create(
SessionInputBuffer buffer, MessageConstraints constraints) {
NHttpMessageParser<HttpRequest> customParser = <...>
return customParser;
}
};
NHttpConnectionFactory<DefaultNHttpServerConnection> cf =
new DefaultNHttpServerConnectionFactory(
null,
requestParserFactory,
responseWriterFactory,
ConnectionConfig.DEFAULT);
IOSession iosession = <...>
DefaultNHttpServerConnection conn = cf.createConnection(iosession);
]]></programlisting>
<section>
<title>HTTP header parsing on demand</title>
<para>
The default implementations of <interfacename>HttpMessageParser</interfacename> and
<interfacename>NHttpMessageParser</interfacename> interfaces do not parse HTTP headers
immediately. Parsing of header value is deferred until its properties are accessed.
Those headers that are never used by the application will not be parsed at all. The
<classname>CharArrayBuffer</classname> backing the header can be obtained through an
optional <interfacename>FormattedHeader</interfacename> interface.
</para>
<programlisting><![CDATA[
HttpResponse response = <...>
Header h1 = response.getFirstHeader("Content-Type");
if (h1 instanceof FormattedHeader) {
CharArrayBuffer buf = ((FormattedHeader) h1).getBuffer();
System.out.println(buf);
}
]]></programlisting>
</section>
</section>
</chapter>