| A Simple NIO-based HTTP/HTTPS Server Example |
| |
| |
| INTRODUCTION |
| ============ |
| This directory contains a simple HTTP/HTTPS server. HTTP/HTTPS are two |
| common network protocols that provide for data transfer, and are more |
| fully described in RFC 2616 and RFC 2818 (Available at |
| http://www.ietf.org ). HTTPS is essentially HTTP after the connection |
| has been secured with SSL/TLS. TLS is the successor to SSL, and is |
| described in RFC 2246. |
| |
| This server was written to demonstrate some of the functionality new to |
| the Java 2 platform. The demo is not meant to be a full tutorial, and |
| assumes the reader has some familiarity with the subject matter. |
| |
| In particular, it shows: |
| |
| New I/O (java.nio, java.nio.channels, java.util.regex, java.nio.charset) |
| |
| Introduced in version 1.4 of the platform, NIO was designed to |
| overcome some of the scalability limitations found in the |
| existing blocking java.net.* API's, and to address other |
| concepts such as Regular Expression parsing and Character |
| Sets. |
| |
| This server demonstrates: |
| |
| ByteBuffer |
| Blocking and Non-Blocking I/O |
| SocketChannel |
| ServerSocketChannel |
| Selector |
| CharacterSet |
| Pattern matching using Regular Expressions |
| |
| JSSE (javax.net.ssl) |
| |
| Introduced in version 1.4 of the platform, JSSE provides |
| network security using SSL/TLS for java.net.Socket-based |
| traffic. In version 1.5, the SSLEngine API was introduced |
| which separates the SSL/TLS functionality from the underlying |
| I/O model. By making this separation, applications can adapt |
| I/O and compute strategies to best fit their circumstances. |
| |
| This server demonstrates: |
| |
| Using SSLEngine to create a HTTPS server |
| Creating simple key material for use with HTTPS |
| |
| Concurrency Library (java.util.concurrent) |
| |
| Introduced in version 1.5 of the platform, the concurrency |
| library provides a mechanism which decouples task submission |
| from the mechanics of how each task will be run. |
| |
| This server demonstrates: |
| |
| A ThreadPool with a fixed number of threads, which is |
| based on the number of available processors. |
| |
| |
| SETUP |
| ===== |
| |
| The server must be built on version 1.5 (or later) of the platform. |
| Invoking the following should be sufficient: |
| |
| % mkdir build |
| % javac -source 1.5 -target 1.5 -d build *.java |
| |
| The following creates the document root: |
| |
| % mkdir root |
| |
| All documents should be placed in this directory. |
| |
| For HTTPS, the server authenticates itself to clients by using simple |
| Public Key Infrastructure (PKI) credentials in the form of |
| X509Certificates. You must create the server's credentials before |
| attempting to run the server in "-secure" mode. The server is |
| currently hardcoded to look for its credentials in a file called |
| "testkeys". |
| |
| In this example, we'll create credentials for a fictional widget web |
| site owned by the ubiquitous "Xyzzy, Inc.". When you run this in your |
| own environment, replace "widgets.xyzzy.com" with the hostname of your |
| server. |
| |
| The easiest way to create the SSL/TLS credentials is to use the |
| java keytool, by doing the following: |
| |
| (<CR> represents your end-of-line key) |
| |
| % keytool -genkey -keyalg rsa -keystore testkeys -alias widgets |
| Enter keystore password: passphrase |
| What is your first and last name? |
| [Unknown]: widgets.xyzzy.com<CR> |
| What is the name of your organizational unit? |
| [Unknown]: Consumer Widgets Group<CR> |
| What is the name of your organization? |
| [Unknown]: Xyzzy, Inc.<CR> |
| What is the name of your City or Locality? |
| [Unknown]: Arcata<CR> |
| What is the name of your State or Province? |
| [Unknown]: CA<CR> |
| What is the two-letter country code for this unit? |
| [Unknown]: US<CR> |
| Is CN=widgets.xyzzy.com, OU=Consumer Widgets Group, O="Xyzzy, Inc.", |
| L=Arcata, ST=CA, C=US correct? |
| [no]: yes<CR> |
| |
| Enter key password for <mykey> |
| (RETURN if same as keystore password): <CR> |
| |
| This directory also contain a very simple URL reader (URLDumper), which |
| connects to a specified URL and places all output into a specified file. |
| |
| |
| SERVER EXECUTION |
| ================ |
| |
| % java -classpath build Server N1 |
| |
| Usage: Server <type> [options] |
| type: |
| B1 Blocking/Single-threaded Server |
| BN Blocking/Multi-threaded Server |
| BP Blocking/Pooled-thread Server |
| N1 Nonblocking/Single-threaded Server |
| N2 Nonblocking/Dual-threaded Server |
| |
| options: |
| -port port port number |
| default: 8000 |
| -backlog backlog backlog |
| default: 1024 |
| -secure encrypt with SSL/TLS |
| default is insecure |
| |
| "http://" URLs should be used with insecure mode, and |
| "https://" for secure mode. |
| |
| The "B*" servers use classic blocking I/O: in other words, calls to |
| read()/write() will not return until the I/O operation has completed. The |
| "N*" servers use non-blocking mode and Selectors to determine which |
| Channels are ready to perform I/O. |
| |
| B1: A single-threaded server which completely services each |
| connection before moving to the next. |
| |
| B2: A multi-threaded server which creates a new thread for each |
| connection. This is not efficient for large numbers of |
| connections. |
| |
| BP: A multi-threaded server which creates a pool of threads for use |
| by the server. The Thread pool decides how to schedule those |
| threads. |
| |
| N1: A single-threaded server. All accept() and read()/write() |
| operations are performed by a single thread, but only after |
| being selected for those operations by a Selector. |
| |
| N2: A dual-threaded server which performs accept()s in one thread, and |
| services requests in a second. Both threads use select(). |
| |
| |
| CLIENT EXECUTION |
| ================ |
| You can test the server using any standard browser such as Internet |
| Explorer or Mozilla, but since the browser will not trust the |
| credentials you just created, you may need to accept the credentials |
| via the browser's pop-up dialog box. |
| |
| Alternatively, to use the certificates using the simple included JSSE |
| client URLDumper, export the server certificate into a new truststore, |
| and then run the application using the new truststore. |
| |
| % keytool -export -keystore testkeys -alias widgets -file widgets.cer |
| Enter keystore password: passphrase<CR> |
| Certificate stored in file <widgets.cer> |
| |
| % keytool -import -keystore trustCerts -alias widgetServer \ |
| -file widgets.cer |
| Enter keystore password: passphrase<CR> |
| Owner: CN=widgets.xyzzy.com, OU=Consumer, O="xyzzy, inc.", L=Arcata, |
| ST=CA, C=US |
| Issuer: CN=widgets.xyzzy.com, OU=Consumer, O="xyzzy, inc.", |
| L=Arcata, ST=CA, C=US |
| Serial number: 4086cc7a |
| Valid from: Wed Apr 21 12:33:14 PDT 2004 until: Tue Jul 20 12:33:14 |
| PDT 2004 |
| Certificate fingerprints: |
| MD5: 39:71:42:CD:BF:0D:A9:8C:FB:8B:4A:CD:F8:6D:19:1F |
| SHA1: 69:5D:38:E9:F4:6C:E5:A7:4C:EA:45:8E:FB:3E:F3:9A:84:01:6F:22 |
| Trust this certificate? [no]: yes<CR> |
| Certificate was added to keystore |
| |
| % java -classpath build -Djavax.net.ssl.trustStore=trustCerts \ |
| -Djavax.net.ssl.TrustStorePassword=passphrase \ |
| URLDumper https://widgets.xyzzy.com:8000/ outputFile |
| |
| NOTE: The server must be run with "-secure" in order to receive |
| "https://" URLs. |
| |
| WARNING: This is just a simple example for code exposition, you should |
| spend more time understanding PKI security concerns. |
| |
| |
| SOURCE CODE OVERVIEW |
| ==================== |
| |
| The main class is Server, which handles program startup, and is |
| subclassed by the "B*" and "N*" server classes. |
| |
| Following a successful accept(), the "B*" variants each create a |
| RequestServicer object to perform the actual request/reply operations. The |
| primary differences between the different "B*" servers is how the |
| RequestServicer is actually run: |
| |
| B1: RequestServicer.run() is directly called. |
| BN: A new thread is started, and the thread calls RequestServicer.run(). |
| BP: A ThreadPool is created, and the pool framework is given Runnable |
| tasks to complete. |
| |
| In the "N*" variations, a Dispatcher object is created, which is |
| responsible for performing the select, and then issuing the |
| corresponding handler: |
| |
| N1: A single thread is used for all accept()/read()/write() operations |
| N2: Similar to N1, but a separate thread is used for the accept() |
| operations. |
| |
| In all cases, once the connection has been accepted, a ChannelIO object |
| is created to handle all I/O. In the insecure case, the corresponding |
| SocketChannel methods are directly called. However in the secure case, |
| more manipulations are needed to first secure the channel, then |
| encrypt/decrypt the data, and finally properly send any shutdown |
| messages. ChannelIOSecure extends ChannelIO, and provides the secure |
| variants of the corresponding ChannelIO calls. |
| |
| RequestServicer and RequestHandler are the main drivers for the |
| blocking and non-blocking variants, respectively. They are responsible |
| for: |
| |
| Performing any initial handshaking |
| |
| Reading the request data |
| All data is stored in a local buffer in the ChannelIO |
| structure. |
| |
| Parsing the request |
| The request data is obtained from the ChannelIO object, and |
| is processed by Request class, which represents the |
| parsed URI address. |
| |
| Locating/preparing/sending the data or reporting error conditions. |
| A Reply object is created which represents the entire object to send, |
| including the HTTP/HTTPS headers. |
| |
| Shutdown/closing the channel. |
| |
| |
| CLOSING THOUGHTS |
| ================ |
| This example represents a simple server: it is not production quality. |
| It was primarily meant to demonstrate the new APIs in versions 1.4 and |
| 1.5 of the platform. |
| |
| This example could certainly be expanded to address other areas of |
| concern: for example, assigning multiple threads to handle the selected |
| Channels, or delegating SSLEngine tasks to multiple threads. There are |
| so many ways to implement compute and I/O strategies, we encourage you |
| to experiment and find what works best for your situation. |
| |
| To steal a phrase from many textbooks: |
| |
| "It is left as an exercise for the reader..." |
| |