1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
9
====================================================================
10
Licensed to the Apache Software Foundation (ASF) under one
11
or more contributor license agreements. See the NOTICE file
12
distributed with this work for additional information
13
regarding copyright ownership. The ASF licenses this file
14
to you under the Apache License, Version 2.0 (the
15
"License"); you may not use this file except in compliance
16
with the License. You may obtain a copy of the License at
18
http://www.apache.org/licenses/LICENSE-2.0
20
Unless required by applicable law or agreed to in writing,
21
software distributed under the License is distributed on an
22
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
KIND, either express or implied. See the License for the
24
specific language governing permissions and limitations
26
====================================================================
29
<chapter id="advanced">
30
<title>Advanced topics</title>
32
<title>HTTP message parsing and formatting framework</title>
34
HTTP message processing framework is designed to be expressive and flexible while remaining
35
memory efficient and fast. HttpCore HTTP message processing code achieves near zero
36
intermediate garbage and near zero-copy buffering for its parsing and formatting
37
operations. The same HTTP message parsing and formatting API and implementations are used
38
by both the blocking and non-blocking transport implementations, which helps ensure a
39
consistent behavior of HTTP services regardless of the I/O model.
42
<title>HTTP line parsing and formatting</title>
44
HttpCore utilizes a number of low level components for all its line parsing and
48
<classname>CharArrayBuffer</classname> represents a sequence of characters, usually a
49
single line in an HTTP message stream such as a request line, a status line or a
50
header. Internally <classname>CharArrayBuffer</classname> is backed by an array of
51
chars, which can be expanded to accommodate more input if needed. <classname>
52
CharArrayBuffer</classname> also provides a number of utility methods for manipulating
53
content of the buffer, storing more data and retrieving subsets of data.
55
<programlisting><![CDATA[
56
CharArrayBuffer buf = new CharArrayBuffer(64);
57
buf.append("header: data ");
58
int i = buf.indexOf(':');
59
String s = buf.substringTrimmed(i + 1, buf.length());
60
System.out.println(s);
61
System.out.println(s.length());
63
<para>stdout ></para>
64
<programlisting><![CDATA[
69
<classname>ParserCursor</classname> represents a context of a parsing operation: the
70
bounds limiting the scope of the parsing operation and the current position the parsing
71
operation is expected to start at.
73
<programlisting><![CDATA[
74
CharArrayBuffer buf = new CharArrayBuffer(64);
75
buf.append("header: data ");
76
int i = buf.indexOf(':');
77
ParserCursor cursor = new ParserCursor(0, buf.length());
78
cursor.updatePos(i + 1);
79
System.out.println(cursor);
81
<para>stdout ></para>
82
<programlisting><![CDATA[
86
<interfacename>LineParser</interfacename> is the interface for parsing lines in the
87
head section of an HTTP message. There are individual methods for parsing a request
88
line, a status line, or a header line. The lines to parse are passed in memory, the
89
parser does not depend on any specific I/O mechanism.
91
<programlisting><![CDATA[
92
CharArrayBuffer buf = new CharArrayBuffer(64);
93
buf.append("HTTP/1.1 200");
94
ParserCursor cursor = new ParserCursor(0, buf.length());
96
LineParser parser = new BasicLineParser();
97
ProtocolVersion ver = parser.parseProtocolVersion(buf, cursor);
98
System.out.println(ver);
99
System.out.println(buf.substringTrimmed(
101
cursor.getUpperBound()));
103
<para>stdout ></para>
104
<programlisting><![CDATA[
108
<programlisting><![CDATA[
109
CharArrayBuffer buf = new CharArrayBuffer(64);
110
buf.append("HTTP/1.1 200 OK");
111
ParserCursor cursor = new ParserCursor(0, buf.length());
112
LineParser parser = new BasicLineParser();
113
StatusLine sl = parser.parseStatusLine(buf, cursor);
114
System.out.println(sl.getReasonPhrase());
116
<para>stdout ></para>
117
<programlisting><![CDATA[
121
<interfacename>LineFormatter</interfacename> for formatting elements of the head
122
section of an HTTP message. This is the complement to <interfacename>LineParser
123
</interfacename>. There are individual methods for formatting a request line, a status
124
line, or a header line.
127
Please note the formatting does not include the trailing line break sequence
128
<literal>CR-LF</literal>.
130
<programlisting><![CDATA[
131
CharArrayBuffer buf = new CharArrayBuffer(64);
132
LineFormatter formatter = new BasicLineFormatter();
133
formatter.formatRequestLine(buf,
134
new BasicRequestLine("GET", "/", HttpVersion.HTTP_1_1));
135
System.out.println(buf.toString());
136
formatter.formatHeader(buf,
137
new BasicHeader("Content-Type", "text/plain"));
138
System.out.println(buf.toString());
140
<para>stdout ></para>
141
<programlisting><![CDATA[
143
Content-Type: text/plain
146
<interfacename>HeaderValueParser</interfacename> is the interface for parsing header
147
values into elements.
149
<programlisting><![CDATA[
150
CharArrayBuffer buf = new CharArrayBuffer(64);
151
HeaderValueParser parser = new BasicHeaderValueParser();
152
buf.append("name1=value1; param1=p1, " +
153
"name2 = \"value2\", name3 = value3");
154
ParserCursor cursor = new ParserCursor(0, buf.length());
155
System.out.println(parser.parseHeaderElement(buf, cursor));
156
System.out.println(parser.parseHeaderElement(buf, cursor));
157
System.out.println(parser.parseHeaderElement(buf, cursor));
159
<para>stdout ></para>
160
<programlisting><![CDATA[
161
name1=value1; param1=p1
166
<interfacename>HeaderValueFormatter</interfacename> is the interface for formatting
167
elements of a header value. This is the complement to <interfacename>HeaderValueParser
170
<programlisting><![CDATA[
171
CharArrayBuffer buf = new CharArrayBuffer(64);
172
HeaderValueFormatter formatter = new BasicHeaderValueFormatter();
173
HeaderElement[] hes = new HeaderElement[] {
174
new BasicHeaderElement("name1", "value1",
175
new NameValuePair[] {
176
new BasicNameValuePair("param1", "p1")} ),
177
new BasicHeaderElement("name2", "value2"),
178
new BasicHeaderElement("name3", "value3"),
180
formatter.formatElements(buf, hes, true);
181
System.out.println(buf.toString());
183
<para>stdout ></para>
184
<programlisting><![CDATA[
185
name1="value1"; param1="p1", name2="value2", name3="value3"
189
<title>HTTP message streams and session I/O buffers</title>
191
HttpCore provides a number of utility classes for the blocking and non-blocking I/O
192
models that facilitate the processing of HTTP message streams, simplify handling of
193
<literal>CR-LF</literal> delimited lines in HTTP messages and manage intermediate data
197
HTTP connection implementations usually rely on session input/output buffers for
198
reading and writing data from and to an HTTP message stream. Session input/output
199
buffer implementations are I/O model specific and are optimized either for blocking or
200
non-blocking operations.
203
Blocking HTTP connections use socket bound session buffers to transfer data. Session
204
buffer interfaces are similar to <classname>java.io.InputStream</classname> /
205
<classname>java.io.OutputStream</classname> classes, but they also provide methods for
206
reading and writing <literal>CR-LF</literal> delimited lines.
208
<programlisting><![CDATA[
211
HttpParams params = new BasicHttpParams();
212
SessionInputBuffer inbuffer = new SocketInputBuffer(
213
socket1, 4096, params);
214
SessionOutputBuffer outbuffer = new SocketOutputBuffer(
215
socket2, 4096, params);
217
CharArrayBuffer linebuf = new CharArrayBuffer(1024);
218
inbuffer.readLine(linebuf);
219
outbuffer.writeLine(linebuf);
222
Non-blocking HTTP connections use session buffers optimized for reading and writing
223
data from and to non-blocking NIO channels. NIO session input/output sessions help deal
224
with <literal>CR-LF</literal> delimited lines in a non-blocking I/O mode.
226
<programlisting><![CDATA[
227
ReadableByteChannel channel1;
228
WritableByteChannel channel2;
230
HttpParams params = new BasicHttpParams();
231
SessionInputBuffer inbuffer = new SessionInputBufferImpl(
233
SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(
236
CharArrayBuffer linebuf = new CharArrayBuffer(1024);
237
boolean endOfStream = false;
238
int bytesRead = inbuffer.fill(channel1);
239
if (bytesRead == -1) {
242
if (inbuffer.readLine(linebuf, endOfStream)) {
243
outbuffer.writeLine(linebuf);
245
if (outbuffer.hasData()) {
246
outbuffer.flush(channel2);
251
<title>HTTP message parsers and formatter</title>
253
HttpCore also provides a coarse-grained facade type of interfaces for parsing and
254
formatting of HTTP messages. Default implementations of those interfaces build upon the
255
functionality provided by <interfacename>SessionInputBuffer</interfacename> /
256
<interfacename>SessionOutputBuffer</interfacename> and <interfacename>HttpLineParser
257
</interfacename>/ <interfacename>HttpLineFormatter</interfacename> implementations.
260
Example of HTTP request parsing / writing for blocking HTTP connections:
262
<programlisting><![CDATA[
263
SessionInputBuffer inbuffer;
264
SessionOutputBuffer outbuffer;
266
HttpParams params = new BasicHttpParams();
268
HttpMessageParser requestParser = new HttpRequestParser(
270
new BasicLineParser(),
271
new DefaultHttpRequestFactory(),
274
HttpRequest request = (HttpRequest) requestParser.parse();
276
HttpMessageWriter requestWriter = new HttpRequestWriter(
278
new BasicLineFormatter(),
281
requestWriter.write(request);
284
Example of HTTP response parsing / writing for blocking HTTP connections:
286
<programlisting><![CDATA[
287
SessionInputBuffer inbuffer;
288
SessionOutputBuffer outbuffer;
290
HttpParams params = new BasicHttpParams();
292
HttpMessageParser responseParser = new HttpResponseParser(
294
new BasicLineParser(),
295
new DefaultHttpResponseFactory(),
298
HttpResponse response = (HttpResponse) responseParser.parse();
300
HttpMessageWriter responseWriter = new HttpResponseWriter(
302
new BasicLineFormatter(),
305
responseWriter.write(response);
308
Example of HTTP request parsing / writing for non-blocking HTTP connections:
310
<programlisting><![CDATA[
311
SessionInputBuffer inbuffer;
312
SessionOutputBuffer outbuffer;
314
HttpParams params = new BasicHttpParams();
316
NHttpMessageParser requestParser = new HttpRequestParser(
318
new BasicLineParser(),
319
new DefaultHttpRequestFactory(),
322
HttpRequest request = (HttpRequest) requestParser.parse();
324
NHttpMessageWriter requestWriter = new HttpRequestWriter(
326
new BasicLineFormatter(),
329
requestWriter.write(request);
332
Example of HTTP response parsing / writing for non-blocking HTTP connections:
334
<programlisting><![CDATA[
335
SessionInputBuffer inbuffer;
336
SessionOutputBuffer outbuffer;
338
HttpParams params = new BasicHttpParams();
340
NHttpMessageParser responseParser = new HttpResponseParser(
342
new BasicLineParser(),
343
new DefaultHttpResponseFactory(),
346
HttpResponse response = (HttpResponse) responseParser.parse();
348
NHttpMessageWriter responseWriter = new HttpResponseWriter(
350
new BasicLineFormatter(),
353
responseWriter.write(response);
357
<title>HTTP header parsing on demand</title>
359
The default implementations of <interfacename>HttpMessageParser</interfacename> and
360
<interfacename>NHttpMessageParser</interfacename> interfaces do not parse HTTP headers
361
immediately. Parsing of header value is deferred until its properties are accessed.
362
Those headers that are never used by the application will not be parsed at all. The
363
<classname>CharArrayBuffer</classname> backing the header can be obtained through an
364
optional <interfacename>FormattedHeader</interfacename> interface.
366
<programlisting><![CDATA[
367
Header h1 = response.getFirstHeader("Content-Type");
368
if (h1 instanceof FormattedHeader) {
369
CharArrayBuffer buf = ((FormattedHeader) h1).getBuffer();
370
System.out.println(buf);
376
<title>Customizing HTTP connections</title>
378
One can customize the way HTTP connections parse and format HTTP messages by extending the
379
default implementations and overriding factory methods and replacing the default parser or
380
formatter implementations with a custom one.
383
For blocking HTTP connections one also can provide custom implementation of session
384
input/output buffers.
386
<programlisting><![CDATA[
387
class MyDefaultNHttpClientConnection
388
extends DefaultNHttpClientConnection {
390
public MyDefaultNHttpClientConnection(
392
HttpResponseFactory responseFactory,
393
ByteBufferAllocator allocator,
395
super(session, responseFactory, allocator, params);
399
protected NHttpMessageWriter createRequestWriter(
400
SessionOutputBuffer buffer,
402
return new HttpRequestWriter(
403
buffer, new BasicLineFormatter(), params);
407
protected NHttpMessageParser createResponseParser(
408
SessionInputBuffer buffer,
409
HttpResponseFactory responseFactory,
411
return new HttpResponseParser(
412
buffer, new BasicLineParser(), responseFactory, params);
418
For non-blocking HTTP connection implementation one can replace the default HTTP message
419
parser and formatter implementations. The session input/output buffer implementations can
420
be overridden at the I/O reactor level.
422
<programlisting><![CDATA[
423
class MyDefaultHttpClientConnection
424
extends DefaultHttpClientConnection {
427
protected SessionInputBuffer createSessionInputBuffer(
430
HttpParams params) throws IOException {
431
return new MySocketInputBuffer(socket, buffersize, params);
435
protected SessionOutputBuffer createSessionOutputBuffer(
438
HttpParams params) throws IOException {
439
return new MySocketOutputBuffer(socket, buffersize, params);
443
protected HttpMessageWriter createRequestWriter(
444
SessionOutputBuffer buffer,
446
return new MyHttpRequestWriter(
447
buffer, new BasicLineFormatter(), params);
451
protected HttpMessageParser createResponseParser(
452
SessionInputBuffer buffer,
453
HttpResponseFactory responseFactory,
455
return new MyHttpResponseParser(
456
buffer, new BasicLineParser(), responseFactory, params);