private support for your internal/customer projects ... custom extensions and distributions ... versioned snapshots for indefinite support ... scalability guidance for your apps and Ajax/Comet projects ... development services from 1 day to full product delivery
This page contains content that we have migrated from Jetty 7 or Jetty 8 documentation into the correct format, but we have not yet audited it for technical accuracy in with Jetty 9. Be aware that examples or information contained on this page may be incorrect. Please check back soon as we continue improving the documentation, or submit corrections yourself to this page through Github. Thank you.
Table of Contents
This chapter discusses various options for configuring Jetty.
The WebSockets protocol and API is an emerging standard that seeks to provide high-quality, bidirectional communication between a browser (or other web client) and a server. The goal is to eventually replace Comet techniques like long polling. Jetty has supported the various WebSocket drafts in the 7.x and 8.x releases.
This document shows you how to use WebSockets from the levels closest to the machine; it targets framework developers who want to use WebSockets, and application developers who can't stand not knowing what is under the hood. However, application programmers should not follow these examples to build an application. WebSockets is not a silver bullet, and on its own will never be simple to use for nontrivial applications (see Is WebSocket Chat Simpler?). We recommend that you look toward frameworks like Cometd, that provide a higher level of abstraction, hide the technicalities, and allow you to use either Comet long polling or WebSockets transparently.
The simplest way to get started is to download three Jars: The Jetty aggregate Jar that contains all of
Jetty; the jetty-websocket test Jar that contains a client and server; and the servlet Jar. You can do this using a
browser or with the following command line wgets:
wget -O jetty-all.jar --user-agent=demo \ http://repo2.maven.org/maven2/org/eclipse/jetty/aggregate/jetty-all/7.6.2.v20120308/jetty-all-7.6.2.v20120308.jar wget -O jetty-websocket-tests.jar --user-agent=demo \ http://repo2.maven.org/maven2/org/eclipse/jetty/jetty-websocket/7.6.2.v20120308/jetty-websocket-7.6.2.v20120308-tests.jar wget --user-agent=demo \ http://repo2.maven.org/maven2/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar
To run a simple test server (use –help
to see more options):
java -cp jetty-websocket-tests.jar:jetty-all.jar:servlet-api-2.5.jar \ org.eclipse.jetty.websocket.TestServer \ --port 8080 \ --docroot . \ --verbose
You can test the server with the test client (use –help
to see more options):
java -cp jetty-websocket-tests.jar:jetty-all.jar:servlet-api-2.5.jar \ org.eclipse.jetty.websocket.TestClient \ --port 8080 \ --protocol echo
The output from the test client is similar to ping. You can use the options you discover by using
–help
to try out different types of tests, including fragmentation and aggregation of WebSocket
frames.
Using a Java client is not compelling unless you want to write a desktop application that uses WebSocket (a viable idea). But most WebSocket users want to use the browser as a client. So point your browser at the TestServer at http://localhost:8080.
The WebSocket TestServer also runs an HTTP file server at the directory given by –docroot
,
so in this case you should see in the browser a listing of the directory in which you ran the test server.
To turn the browser into a WebSocket client, you need to serve some HTML and Javascript that executes in the browser and talks back to the server using Websockets.
Create the file index.html
in the same directory you ran the server from.
Put into it the following contents which you can download from jetty-websocket/src/test/webapp/index.html.>This index file contains the HTML, CSS and Javascript for a basic chat room.
You should now be able to point your browser(s) at the test server, see a chat room, and join it. If your browser does not support WebSockets, you’ll receive a warning.
The initial HTML view has a prompt for a user name. When a you enter a name, the system calls the
join
method, which creates the WebSocket to the server. The URI for the WebSocket derives from the
document's location. Call back functions are registered for open, message and close events. The
org.ietf.websocket.test-echo-broadcast
subprotocol is specified as this echoes all received messages to all
other broadcast connections, providing the semantic a chat room requires:
java -cp je join: function(name) { this._username=name; var location = document.location.toString().replace('http://','ws://').replace('https://','wss://'); this._ws=new WebSocket(location,"org.ietf.websocket.test-echo-broadcast"); this._ws.onopen=this._onopen; this._ws.onmessage=this._onmessage; this._ws.onclose=this._onclose; },
When the WebSocket successfully connects to the server, it calls the onopen
callback, which
changes the appearance of the chat room to prompt for a chat message. It also sends a message saying the user has
joined the room:
_onopen: function(){ $('join').className='hidden'; $('joined').className=''; $('phrase').focus(); room._send(room._username,'has joined!'); },
To send a message, you format a string as username:chat text
and then call the WebSocket
send
method:
_send: function(user,message){ user=user.replace(':','_'); if (this._ws) this._ws.send(user+':'+message); }, chat: function(text) { if (text != null && text.length>0 ) room._send(room._username,text); },
When the browser receives a WebSocket message over the connection, the system calls the
onmessage
callback with a message object. The Jetty implementation looks for the username and colon, strips
out any markup, and then appends the message to the chat room:
_onmessage: function(m) { if (m.data){ var c=m.data.indexOf(':'); var from=m.data.substring(0,c).replace('<','<').replace('>','>'); var text=m.data.substring(c+1).replace('<','<').replace('>','>'); var chat=$('chat'); var spanFrom = document.createElement('span'); spanFrom.className='from'; spanFrom.innerHTML=from+': '; var spanText = document.createElement('span'); spanText.className='text'; spanText.innerHTML=text; var lineBreak = document.createElement('br'); chat.appendChild(spanFrom); chat.appendChild(spanText); chat.appendChild(lineBreak); chat.scrollTop = chat.scrollHeight - chat.clientHeight; } },
If the server closes the connection, or if the browser times it out, then the onclose
callback
is called. This nulls out the chat room and reverts to the starting position:
onclose: function(m) { this._ws=null; $('join').className=''; $('joined').className='hidden'; $('username').focus(); $('chat').innerHTML=''; }
The TestServer.java server side code for this chat room uses an embedded Jetty server and is written against the Jetty WebSocket APIs that are not part of the WebSocket standard. There is not yet even a proposed standard for serverside WebSocket APIs, but it is a topic for consideration with the servlet 3.1 JSR.
The test server is an extension of an embedded Jetty server, and the constructor adds a connector at the required port, creates a WebSocketHandler and a ResourceHandler" and chains them together:
public TestServer(int port) { _connector = new SelectChannelConnector(); _connector.setPort(port); addConnector(_connector); _wsHandler = new WebSocketHandler() { public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { ... return _websocket; } }; setHandler(_wsHandler); _rHandler=new ResourceHandler(); _rHandler.setDirectoriesListed(true); _rHandler.setResourceBase(_docroot); _wsHandler.setHandler(_rHandler); }
The resource handler is responsible for serving the static content like HTML and Javascript. The
WebSocketHandler looks for WebSocket handshake requests and handles them by calling the
doWebSocketConnect
method, which we have extended to create a WebSocket depending on the sub protocol
passed:
_wsHandler = new WebSocketHandler() { public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { if ("org.ietf.websocket.test-echo".equals(protocol) || "echo".equals(protocol) || "lws-mirror-protocol".equals(protocol)) _websocket = new TestEchoWebSocket(); else if ("org.ietf.websocket.test-echo-broadcast".equals(protocol)) _websocket = new TestEchoBroadcastWebSocket(); else if ("org.ietf.websocket.test-echo-assemble".equals(protocol)) _websocket = new TestEchoAssembleWebSocket(); else if ("org.ietf.websocket.test-echo-fragment".equals(protocol)) _websocket = new TestEchoFragmentWebSocket(); else if (protocol==null) _websocket = new TestWebSocket(); return _websocket; } };
A simplification of the test
WebSocket from the test server follows. It excludes the shared code for the other protocols supported. Like
the Javascript API, there is an onOpen, onClose
and onMessage
callback. The
onOpen
callback is passed in a
Connection instance that sends messages. The implementation of onOpen
adds the WebSocket to a
collection of all known WebSockets, and onClose
removes the WebSocket. The implementation of
onMessage
is to simply iterate through that collection and to send the received message to each
WebSocket:
ConcurrentLinkedQueue _broadcast = new ConcurrentLinkedQueue(); class TestEchoBroadcastWebSocket implements WebSocket.OnTextMessage { protected Connection _connection; public void onOpen(Connection connection) { _connection=connection; _broadcast.add(this); } public void onClose(int code,String message) { _broadcast.remove(this); } public void onMessage(final String data) { for (TestEchoBroadcastWebSocket ws : _broadcast) { try { ws._connection.sendMessage(data); } catch (IOException e) { _broadcast.remove(ws); e.printStackTrace(); } } } }
Now you know the basics of how WebSockets work, we repeat the warning that you should not do it this way–unless you are a framework developer. Even then, you probably want to use the WebSocketServlet and a non embedded Jetty, but the basic concepts are the same. The strength of the Jetty solution is that it terminates both WebSocket connections and HTTP requests in the same environment, so that mixed frameworks and applications are easy to create.
Application developers should really look to a framework like Cometd rather than directly coding to WebSockets themselves. It is not that the mechanics of WebSockets are hard, just that they don’t solve all of the problems that you encounter in a real world Comet application.
See an error or something missing? Contribute to this documentation at Github!