J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 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 | |