2
|
1 |
A Simple NIO-based HTTP/HTTPS Server Example
|
|
2 |
|
|
3 |
|
|
4 |
INTRODUCTION
|
|
5 |
============
|
|
6 |
This directory contains a simple HTTP/HTTPS server. HTTP/HTTPS are two
|
|
7 |
common network protocols that provide for data transfer, and are more
|
|
8 |
fully described in RFC 2616 and RFC 2818 (Available at
|
|
9 |
http://www.ietf.org ). HTTPS is essentially HTTP after the connection
|
|
10 |
has been secured with SSL/TLS. TLS is the successor to SSL, and is
|
|
11 |
described in RFC 2246.
|
|
12 |
|
|
13 |
This server was written to demonstrate some of the functionality new to
|
|
14 |
the Java 2 platform. The demo is not meant to be a full tutorial, and
|
|
15 |
assumes the reader has some familiarity with the subject matter.
|
|
16 |
|
|
17 |
In particular, it shows:
|
|
18 |
|
|
19 |
New I/O (java.nio, java.nio.channels, java.util.regex, java.nio.charset)
|
|
20 |
|
|
21 |
Introduced in version 1.4 of the platform, NIO was designed to
|
|
22 |
overcome some of the scalability limitations found in the
|
|
23 |
existing blocking java.net.* API's, and to address other
|
|
24 |
concepts such as Regular Expression parsing and Character
|
|
25 |
Sets.
|
|
26 |
|
|
27 |
This server demonstrates:
|
|
28 |
|
|
29 |
ByteBuffer
|
|
30 |
Blocking and Non-Blocking I/O
|
|
31 |
SocketChannel
|
|
32 |
ServerSocketChannel
|
|
33 |
Selector
|
|
34 |
CharacterSet
|
|
35 |
Pattern matching using Regular Expressions
|
|
36 |
|
|
37 |
JSSE (javax.net.ssl)
|
|
38 |
|
|
39 |
Introduced in version 1.4 of the platform, JSSE provides
|
|
40 |
network security using SSL/TLS for java.net.Socket-based
|
|
41 |
traffic. In version 1.5, the SSLEngine API was introduced
|
|
42 |
which separates the SSL/TLS functionality from the underlying
|
|
43 |
I/O model. By making this separation, applications can adapt
|
|
44 |
I/O and compute strategies to best fit their circumstances.
|
|
45 |
|
|
46 |
This server demonstrates:
|
|
47 |
|
|
48 |
Using SSLEngine to create a HTTPS server
|
|
49 |
Creating simple key material for use with HTTPS
|
|
50 |
|
|
51 |
Concurrency Library (java.util.concurrent)
|
|
52 |
|
|
53 |
Introduced in version 1.5 of the platform, the concurrency
|
|
54 |
library provides a mechanism which decouples task submission
|
|
55 |
from the mechanics of how each task will be run.
|
|
56 |
|
|
57 |
This server demonstrates:
|
|
58 |
|
|
59 |
A ThreadPool with a fixed number of threads, which is
|
|
60 |
based on the number of available processors.
|
|
61 |
|
|
62 |
|
|
63 |
SETUP
|
|
64 |
=====
|
|
65 |
|
|
66 |
The server must be built on version 1.5 (or later) of the platform.
|
|
67 |
Invoking the following should be sufficient:
|
|
68 |
|
|
69 |
% mkdir build
|
|
70 |
% javac -source 1.5 -target 1.5 -d build *.java
|
|
71 |
|
|
72 |
The following creates the document root:
|
|
73 |
|
|
74 |
% mkdir root
|
|
75 |
|
|
76 |
All documents should be placed in this directory.
|
|
77 |
|
|
78 |
For HTTPS, the server authenticates itself to clients by using simple
|
|
79 |
Public Key Infrastructure (PKI) credentials in the form of
|
|
80 |
X509Certificates. You must create the server's credentials before
|
|
81 |
attempting to run the server in "-secure" mode. The server is
|
|
82 |
currently hardcoded to look for its credentials in a file called
|
|
83 |
"testkeys".
|
|
84 |
|
|
85 |
In this example, we'll create credentials for a fictional widget web
|
|
86 |
site owned by the ubiquitous "Xyzzy, Inc.". When you run this in your
|
|
87 |
own environment, replace "widgets.xyzzy.com" with the hostname of your
|
|
88 |
server.
|
|
89 |
|
|
90 |
The easiest way to create the SSL/TLS credentials is to use the
|
|
91 |
java keytool, by doing the following:
|
|
92 |
|
|
93 |
(<CR> represents your end-of-line key)
|
|
94 |
|
|
95 |
% keytool -genkey -keyalg rsa -keystore testkeys -alias widgets
|
|
96 |
Enter keystore password: passphrase
|
|
97 |
What is your first and last name?
|
|
98 |
[Unknown]: widgets.xyzzy.com<CR>
|
|
99 |
What is the name of your organizational unit?
|
|
100 |
[Unknown]: Consumer Widgets Group<CR>
|
|
101 |
What is the name of your organization?
|
|
102 |
[Unknown]: Xyzzy, Inc.<CR>
|
|
103 |
What is the name of your City or Locality?
|
|
104 |
[Unknown]: Arcata<CR>
|
|
105 |
What is the name of your State or Province?
|
|
106 |
[Unknown]: CA<CR>
|
|
107 |
What is the two-letter country code for this unit?
|
|
108 |
[Unknown]: US<CR>
|
|
109 |
Is CN=widgets.xyzzy.com, OU=Consumer Widgets Group, O="Xyzzy, Inc.",
|
|
110 |
L=Arcata, ST=CA, C=US correct?
|
|
111 |
[no]: yes<CR>
|
|
112 |
|
|
113 |
Enter key password for <mykey>
|
|
114 |
(RETURN if same as keystore password): <CR>
|
|
115 |
|
|
116 |
This directory also contain a very simple URL reader (URLDumper), which
|
|
117 |
connects to a specified URL and places all output into a specified file.
|
|
118 |
|
|
119 |
|
|
120 |
SERVER EXECUTION
|
|
121 |
================
|
|
122 |
|
|
123 |
% java -classpath build Server N1
|
|
124 |
|
|
125 |
Usage: Server <type> [options]
|
|
126 |
type:
|
|
127 |
B1 Blocking/Single-threaded Server
|
|
128 |
BN Blocking/Multi-threaded Server
|
|
129 |
BP Blocking/Pooled-thread Server
|
|
130 |
N1 Nonblocking/Single-threaded Server
|
|
131 |
N2 Nonblocking/Dual-threaded Server
|
|
132 |
|
|
133 |
options:
|
|
134 |
-port port port number
|
|
135 |
default: 8000
|
|
136 |
-backlog backlog backlog
|
|
137 |
default: 1024
|
|
138 |
-secure encrypt with SSL/TLS
|
|
139 |
default is insecure
|
|
140 |
|
|
141 |
"http://" URLs should be used with insecure mode, and
|
|
142 |
"https://" for secure mode.
|
|
143 |
|
|
144 |
The "B*" servers use classic blocking I/O: in other words, calls to
|
|
145 |
read()/write() will not return until the I/O operation has completed. The
|
|
146 |
"N*" servers use non-blocking mode and Selectors to determine which
|
|
147 |
Channels are ready to perform I/O.
|
|
148 |
|
|
149 |
B1: A single-threaded server which completely services each
|
|
150 |
connection before moving to the next.
|
|
151 |
|
|
152 |
B2: A multi-threaded server which creates a new thread for each
|
|
153 |
connection. This is not efficient for large numbers of
|
|
154 |
connections.
|
|
155 |
|
|
156 |
BP: A multi-threaded server which creates a pool of threads for use
|
|
157 |
by the server. The Thread pool decides how to schedule those
|
|
158 |
threads.
|
|
159 |
|
|
160 |
N1: A single-threaded server. All accept() and read()/write()
|
|
161 |
operations are performed by a single thread, but only after
|
|
162 |
being selected for those operations by a Selector.
|
|
163 |
|
|
164 |
N2: A dual-threaded server which performs accept()s in one thread, and
|
|
165 |
services requests in a second. Both threads use select().
|
|
166 |
|
|
167 |
|
|
168 |
CLIENT EXECUTION
|
|
169 |
================
|
|
170 |
You can test the server using any standard browser such as Internet
|
|
171 |
Explorer or Mozilla, but since the browser will not trust the
|
|
172 |
credentials you just created, you may need to accept the credentials
|
|
173 |
via the browser's pop-up dialog box.
|
|
174 |
|
|
175 |
Alternatively, to use the certificates using the simple included JSSE
|
|
176 |
client URLDumper, export the server certificate into a new truststore,
|
|
177 |
and then run the application using the new truststore.
|
|
178 |
|
|
179 |
% keytool -export -keystore testkeys -alias widgets -file widgets.cer
|
|
180 |
Enter keystore password: passphrase<CR>
|
|
181 |
Certificate stored in file <widgets.cer>
|
|
182 |
|
|
183 |
% keytool -import -keystore trustCerts -alias widgetServer \
|
|
184 |
-file widgets.cer
|
|
185 |
Enter keystore password: passphrase<CR>
|
|
186 |
Owner: CN=widgets.xyzzy.com, OU=Consumer, O="xyzzy, inc.", L=Arcata,
|
|
187 |
ST=CA, C=US
|
|
188 |
Issuer: CN=widgets.xyzzy.com, OU=Consumer, O="xyzzy, inc.",
|
|
189 |
L=Arcata, ST=CA, C=US
|
|
190 |
Serial number: 4086cc7a
|
|
191 |
Valid from: Wed Apr 21 12:33:14 PDT 2004 until: Tue Jul 20 12:33:14
|
|
192 |
PDT 2004
|
|
193 |
Certificate fingerprints:
|
|
194 |
MD5: 39:71:42:CD:BF:0D:A9:8C:FB:8B:4A:CD:F8:6D:19:1F
|
|
195 |
SHA1: 69:5D:38:E9:F4:6C:E5:A7:4C:EA:45:8E:FB:3E:F3:9A:84:01:6F:22
|
|
196 |
Trust this certificate? [no]: yes<CR>
|
|
197 |
Certificate was added to keystore
|
|
198 |
|
|
199 |
% java -classpath build -Djavax.net.ssl.trustStore=trustCerts \
|
|
200 |
-Djavax.net.ssl.TrustStorePassword=passphrase \
|
|
201 |
URLDumper https://widgets.xyzzy.com:8000/ outputFile
|
|
202 |
|
|
203 |
NOTE: The server must be run with "-secure" in order to receive
|
|
204 |
"https://" URLs.
|
|
205 |
|
|
206 |
WARNING: This is just a simple example for code exposition, you should
|
|
207 |
spend more time understanding PKI security concerns.
|
|
208 |
|
|
209 |
|
|
210 |
SOURCE CODE OVERVIEW
|
|
211 |
====================
|
|
212 |
|
|
213 |
The main class is Server, which handles program startup, and is
|
|
214 |
subclassed by the "B*" and "N*" server classes.
|
|
215 |
|
|
216 |
Following a successful accept(), the "B*" variants each create a
|
|
217 |
RequestServicer object to perform the actual request/reply operations. The
|
|
218 |
primary differences between the different "B*" servers is how the
|
|
219 |
RequestServicer is actually run:
|
|
220 |
|
|
221 |
B1: RequestServicer.run() is directly called.
|
|
222 |
BN: A new thread is started, and the thread calls RequestServicer.run().
|
|
223 |
BP: A ThreadPool is created, and the pool framework is given Runnable
|
|
224 |
tasks to complete.
|
|
225 |
|
|
226 |
In the "N*" variations, a Dispatcher object is created, which is
|
|
227 |
responsible for performing the select, and then issuing the
|
|
228 |
corresponding handler:
|
|
229 |
|
|
230 |
N1: A single thread is used for all accept()/read()/write() operations
|
|
231 |
N2: Similar to N1, but a separate thread is used for the accept()
|
|
232 |
operations.
|
|
233 |
|
|
234 |
In all cases, once the connection has been accepted, a ChannelIO object
|
|
235 |
is created to handle all I/O. In the insecure case, the corresponding
|
|
236 |
SocketChannel methods are directly called. However in the secure case,
|
|
237 |
more manipulations are needed to first secure the channel, then
|
|
238 |
encrypt/decrypt the data, and finally properly send any shutdown
|
|
239 |
messages. ChannelIOSecure extends ChannelIO, and provides the secure
|
|
240 |
variants of the corresponding ChannelIO calls.
|
|
241 |
|
|
242 |
RequestServicer and RequestHandler are the main drivers for the
|
|
243 |
blocking and non-blocking variants, respectively. They are responsible
|
|
244 |
for:
|
|
245 |
|
|
246 |
Performing any initial handshaking
|
|
247 |
|
|
248 |
Reading the request data
|
|
249 |
All data is stored in a local buffer in the ChannelIO
|
|
250 |
structure.
|
|
251 |
|
|
252 |
Parsing the request
|
|
253 |
The request data is obtained from the ChannelIO object, and
|
|
254 |
is processed by Request class, which represents the
|
|
255 |
parsed URI address.
|
|
256 |
|
|
257 |
Locating/preparing/sending the data or reporting error conditions.
|
|
258 |
A Reply object is created which represents the entire object to send,
|
|
259 |
including the HTTP/HTTPS headers.
|
|
260 |
|
|
261 |
Shutdown/closing the channel.
|
|
262 |
|
|
263 |
|
|
264 |
CLOSING THOUGHTS
|
|
265 |
================
|
|
266 |
This example represents a simple server: it is not production quality.
|
|
267 |
It was primarily meant to demonstrate the new APIs in versions 1.4 and
|
|
268 |
1.5 of the platform.
|
|
269 |
|
|
270 |
This example could certainly be expanded to address other areas of
|
|
271 |
concern: for example, assigning multiple threads to handle the selected
|
|
272 |
Channels, or delegating SSLEngine tasks to multiple threads. There are
|
|
273 |
so many ways to implement compute and I/O strategies, we encourage you
|
|
274 |
to experiment and find what works best for your situation.
|
|
275 |
|
|
276 |
To steal a phrase from many textbooks:
|
|
277 |
|
|
278 |
"It is left as an exercise for the reader..."
|
|
279 |
|