17 Using WebSockets in WebLogic Server

This chapter describes the implementation of the WebSocket Protocol in WebLogic Server.

This chapter includes the following sections:

Understanding WebSockets

WebLogic Server supports the WebSocket Protocol (RFC 6455), which provides simultaneous two-way communication over a single TCP connection between clients and servers, where each side can send data independently from the other. To initiate the WebSocket connection, the client sends a handshake request to the server. The connection is established if the handshake request passes validation, and the server accepts the request. When a WebSocket connection is created, a browser client can send data to a WebLogic Server instance while simultaneously receiving data from that server instance. As part of the HTML5 specification (http://www.w3.org/TR/html5/), the WebSocket Protocol is supported by most browsers. For general information about the WebSocket Protocol, see http://tools.ietf.org/html/rfc6455.

If the WebLogic Server Examples component is installed and configured on your machine, you can use the HTML5 WebSocket example for a demonstration of using WebSockets in WebLogic Server. For more information about running this example, see "Sample Applications and Code Examples" in Understanding Oracle WebLogic Server.

The following sections describe the life cycle of a WebSocket in WebLogic Server:

Handshake Request

All WebSocket connections are initiated by the client. To establish a WebSocket connection, the client must send a handshake request (an HTTP GET request) to the server. If the HTTP request matches the pathPatterns attribute of the @WebSocket annotation, then the internal WebSocket servlet validates the handshake request. The validation process follows application validation (implemented in the accept method of the WebSocket listener) and the rules defined in the WebSocket Protocol. If the handshake request is valid, then the servlet upgrades the HTTP request to a WebSocket connection, and the server sends a WebSocket handshake response to the client. If the request is invalid, then the servlet rejects the request.

Reading and Sending Messages

After the WebSocket connection is established, the server frees the request thread and listens for read events on the connection. When the server detects a read event, the server decodes the message, as defined in the WebSocket Protocol, and invokes the corresponding methods of the listener. For example, the server invokes onMessage methods if the message is a text or binary message, onFragment methods if the message is a fragment, and so on. The Work Manager specified in the dispatchPolicy attribute of the @WebSocket annotation controls the thread that invokes these methods.

At the same time, messages can be sent to the WebSocket by invoking the corresponding methods of the WebSocketConnection interface until the WebSocket is closed. For example:

  • send methods send messages to a WebSocket.

  • sendPing and sendPong methods send Ping and Pong control frame.

  • stream methods send message fragments.

The context associated with a WebSocket is tied to that specific WebSocket. Sending messages is a synchronous task and occurs on the thread that issues the call.

Timeout Period

A WebSocket times out if no read or write activity occurs and no Ping messages are received within the configured timeout period. The container enforces a 30-second timeout period as the default. If the timeout period is set to -1, no timeout period is set for the connection.

Upon timeout, the container invokes the listener's onTimeout() method, at which time the listener sends an appropriate message to the client. After the onTimeout() method returns, the container closes the WebSocket.

Connection Closure

At any time during WebSocket communication, the client or server can terminate the WebSocket connection. If the client issues a close request, the container invokes the listener's onClose() method and terminates the underlying connection. From the server side, you can use the WebSocketConnection.close methods to close the WebSocket connection. Upon invoking these methods, the server issues a close request to the client before it closes the connection.

Concurrency and Message Ordering

After upgrading to a WebSocket connection, messages can flow asynchronously in either direction. However, to prevent message corruption, message sending must be synchronous. When multiple threads attempt to send messages on the same WebSocket, ordering is not guaranteed; the thread that first acquires the lock sends the first message.

The WebSocketConnection methods that send messages to the client are not thread safe. If multiple threads use the same instance of the WebSocketConnection methods to send messages, the application is responsible for using the appropriate lock mechanism to send messages synchronously. Otherwise, messages may be corrupted.

Using WebSockets in WebLogic Server

In WebLogic Server, you can use the WebSocket Protocol implementation and accompanying programming API to develop and deploy applications that communicate bidirectionally with clients. Although you can use WebSockets for any type of client-server communication, the implementation is most commonly used to communicate with browsers running Web pages that use the WebSocket API.

The following sections describe using WebSockets in WebLogic Server:

Understanding the WebLogic Server WebSockets Implementation

The WebLogic Server WebSockets implementation includes the following components:

WebSocket Protocol Implementation

The WebSocket Protocol handles connection upgrades, establishes and manages connections, and handles exchanges with the client. Using the existing WebLogic Server threading and network infrastructure, WebLogic Server fully integrates the WebSockets Protocol in its implementation.

WebLogic WebSocket Java API

You can use the WebLogic WebSocket API weblogic.websocket package to develop WebSocket applications. Table 17-1 lists and summarizes the classes and interfaces included in this API. For complete information, see weblogic.websocket in the Java API Reference for Oracle WebLogic Server.

Table 17-1 WebLogic WebSocket Java API Interfaces and Classes

Name Description

weblogic.websocket.WebSocketListener

Interface that receives and handles WebSocket messages. The container is responsible for wiring the listener to the corresponding servlet, which handles the HTTP upgrade request to a WebSocket.

weblogic.websocket.WebSocketConnection

Interface that represents the WebSocket connection between the client and the server. An application uses WebSocketConnection methods to manipulate the connection and send messages to the client.

weblogic.websocket.WebSocketContext

Interface that provides context information for a set of WebSocket connections handled by a WebSocket listener. This interface also provides context information and associated settings for a WebSocket endpoint declared by the @WebSocket annotation or set by the application.

From the WebSocketContext object, you can access the ServletContext object of the Web application. The WebSocketContext object provides configuration information, such as timeout seconds of the WebSocket connection, the maximum size of open WebSocket connections, and the maximum number of messages that can be received or sent across a WebSocket connection. An application can also obtain all of the open WebSocket connections from the WebSocketContext interface and broadcast messages by iterating each WebSocket connection.

weblogic.websocket.WebSocketAdapter

Class that provides a convenient, empty implementation of the WebSocketListener interface. In this class, almost all of the callback methods are implemented as empty methods. Applications can extend this class and then override specific callback methods as needed, instead of implementing the entire WebSocketListener interface.

weblogic.websocket.WSHandshakeRequest

Class that defines an object to provide WebSocket opening handshake request information to a WebSocket listener.

weblogic.websocket.WSHandshakeResponse

Class that defines an object that assists in sending the opening handshake response to the client.


weblogic.websocket.annotation.WebSocket

The @WebSocket annotation declares a WebSocket endpoint that communicates using the WebSocket Protocol. Use the @WebSocket annotation to mark a class as a WebSocket listener that is ready to be exposed as a WebSocket endpoint on which read events are processed. The class annotated by the @WebSocket annotation must implement the WebSocketListener interface or extend from the WebSocketAdapter class. For more information, see weblogic.websocket.annotation.WebSocket in the Java API Reference for Oracle WebLogic Server.

Example 17-1 demonstrates using the @WebSocket annotation:

Example 17-1 Using the @WebSocket Annotation

@WebSocket (
    pathPatterns = {"/chat/*"},
    timeout = 30,
    maxConnections = 1000)
 
public class MyListener implements WebSocketListener {
    ...
}

Building Applications Using the WebLogic WebSocket API

You can use the WebLogic WebSocket API to create browser-based applications that require two-way communication with servers without relying on having multiple HTTP connections open.

WebLogic Server provides the WebLogic WebSocket API within the wlserver/server/lib/wls-api.jar file. To build applications using the WebLogic WebSocket API, define this library in the classpath when compiling the application.

You can also use Maven to build applications using the WebLogic WebSocket API. For more information, see "Using the WebLogic Development Maven Plug-In".

Deploying WebSocket Applications

In WebLogic Server, you deploy WebSocket applications as part of standard Java EE Web application archives (WARs), either as standalone Web applications or WAR modules within enterprise applications. When you deploy the Web application, WebLogic Server detects the @WebSocket annotation on a class and automatically establishes it as a WebSocket endpoint.

You do not need to configure the WebSocket endpoint in the web.xml file, or any other deployment descriptor, or perform any type of dynamic operation to register or enable the WebSocket endpoint.

Using WebSockets with Proxy Servers

Clients accessing WebSocket applications must either connect directly to the WebLogic Server instance or through a Web proxy server that supports the WebSockets Protocol.

Currently, the only Oracle proxy server that supports WebSockets is Oracle Traffic Director. If you use a different proxy server to access WebSocket applications, then ensure that the proxy server supports WebSockets.

Client Programming

A WebSocket client application is typically a composite of HTML5 technologies, including the HTML markup, CSS3, and JavaScript that makes use of the WebSocket JavaScript API. Oracle does not provide a WebSocket JavaScript API for WebSocket clients. However, most browsers support a standard WebSocket API that can be used to create and work with WebSockets. For more information about HTML5, see http://www.w3.org/TR/html5/.

The following steps show an example of the execution flow on a client that is sending messages to a WebLogic Server instance using the WebSockets Protocol.

  1. The client opens a WebSocket connection to the server hosting the WebSocket endpoint, using the ws:// or wss:// protocol prefix. For more information, see "Establishing Secure WebSocket Connections".

    url = ((window.location.protocol == "https:") ? "wss:" : "ws:") 
    + "//" + window.location.host 
    + "/websocket-helloworld-wls/helloworld_delay.ws";
     
    ws = new WebSocket(url);
    
  2. The client registers listeners with the WebSocket object to respond to events, such as opening, closing, and receiving messages. Based on the event and the information received, the client performs the appropriate action.

    ws.onopen = function(event) {
        document.getElementById("status").innerHTML = "OPEN"
    }
     
    ws.onmessage = function(event) {
        msg = event.data
          document.getElementById("short_msg").innerHTML =
          event.data;
    }
    
  3. The client sends messages to the server over the WebSocket object as needed by the application.

    function sendMsg() {
        // Check if connection is open before sending
        if(ws == null || ws.readyState != 1) {
            document.getElementById("reason").innerHTML 
            = "Not connected can't send msg"
        } else {
            ws.send(document.getElementById("name").value);
        }
    }
     
    <input id="send_button" class="button" type="button" value="send" onclick="sendMsg()"/>
    

Broadcasting Messages to WebSocket Clients

In some scenarios, applications must send messages to all of the open WebSocket connections. For example, a stock application must send stock prices to connected WebSocket clients, or a chat application must send messages from one user to all of the other clients in the same chat room.

In order to broadcast messages to all of the WebSocket clients, the application must first determine all of the open WebSocket connections using the getWebSocketConnections() method in the WebSocketContext interface. You can then iterate each WebSocketConnection to send the message to each client.

The following brief code example demonstrates broadcasting messages to clients:

import weblogic.websocket.WebSocketListener;
import weblogic.websocket.WebSocketConnection;
import weblogic.websocket.WebSocketContext;
 
@WebSocket(pathPatterns={"/demo"})
public class MyListener implements WebSocketListener {
  …
     
  public void broadcast(String message) {
    for(WebSocketConnection conn :
        getWebSocketContext().getWebSocketConnections()) {
      try {
        conn.send(message);
      } catch (IOException ioe) {
        // handle the error condition.
      }
    }
  }
}

For real-world applications, two important aspects must be considered that are not addressed in the previous code example:

Efficiency

If too many WebSocket connections are open, then using one thread to broadcast messages is inefficient, because the time it takes for a client to receive a message depends on its location in the iteration process. If thousands of WebSocket connections are open, then iteration is slow, causing some clients to receive messages early and other clients to receive messages much later. This delay is unacceptable in certain situations; for example, a stock application should ensure that each client receives stock price data as early as possible.

To increase efficiency, the application can partition open WebSocket connections into groups and then use multiple threads to broadcast messages to each group of WebSocket connections.

Concurrency

The implementation of the WebSocketConnection interface is not thread safe. If multiple threads send messages on the same WebSocketConnection instance without a proper lock mechanism, then the messages could have issues. Problematic messages could cause connections to close unexpectedly because the client cannot correctly interpret a message.

Applications should choose the appropriate lock mechanism to ensure that when one thread sends messages on a particular WebSocketConnection instance, no other threads can send messages on the same WebSocketConnection instance.

Securing WebSocket Applications

In WebLogic Server, you deploy WebSocket applications as Web application archives (WARs), either as standalone Web applications or WAR modules within enterprise applications. Therefore, many security practices that you apply to securing Web applications can apply to WebSocket applications. For information about Web application security, see "Developing Secure Web Applications" in Developing Applications with the WebLogic Security Service.

The following sections describe security considerations for WebSocket applications in WebLogic Server:

Applying Verified-Origin Policies

Modern browsers use same-origin policies to prevent scripts that are running on Web pages loaded from one origin from interacting with resources from a different origin. The WebSocket Protocol (RFC 6455) uses a verified-origin policy that enables the server to decide whether or not to consent to a cross-origin connection.

When a script sends an opening handshake request to a WebSocket application, an Origin HTTP header is sent with the WebSocket handshake request. If the application does not verify the Origin, then it accepts connections from any origin. If the application is configured not to accept connections from origins other than the expected origin, then the WebSocket application can reject the connection. You can ensure that the WebSocket application verifies the Origin through the accept method of the WebSocketListener implementation class.

The following code example demonstrates applying a verified-origin policy:

import weblogic.websocket.WebSocketListener;
import weblogic.websocket.WSHandshakeRequest;
import weblogic.websocket.WSHandshakeResponse;
 
@WebSocket(pathPatterns={"/demo"})
public class MyListener implements WebSocketListener {
    private static final String ORIGIN = "http://www.example.com:7001";
    public boolean accept(WSHandshakeRequest request, WSHandshakeResponse response) {
      return ORIGIN.equals(request.getOrigin());
    }
    …
}

Note:

Nonbrowser clients (for example, Java Client) are not required to send an Origin HTTP header with the WebSocket handshake request. If a WebSocket handshake request does not include an Origin header, then the request is from a nonbrowser client; if a handshake request includes an Origin header, then the request may be from either a browser or a nonbrowser client.

Because nonbrowser clients can send arbitrary Origin headers, the browser origin security model is not recommended for nonbrowser clients.

Using WebSocket Client Authentication and Authorization

The WebSocket Protocol (RFC 6455) does not specify an authentication method for WebSocket clients during the handshake process. You can use standard Web container authentication and authorization functionality to prevent unauthorized clients from opening WebSocket connections on the server.

Authentication

All configurations of the <auth-method> element that are supported for Web applications can also be used for WebSocket applications. These authentication types include BASIC, FORM, CLIENT-CERT, and so on. For more information, see "Developing Secure Web Applications" in Developing Applications with the WebLogic Security Service.

You can secure the pathPatterns attribute by configuring the relevant <security-constraint> element in the web.xml deployment descriptor file of the WebSocket application. By configuring <security-constraint>, clients must authenticate themselves before sending WebSocket handshake requests. Otherwise, the server rejects the WebSocket handshake request. For more information about the <security-constraint> element, see "web.xml Deployment Descriptor Elements" in Developing Web Applications, Servlets, and JSPs for Oracle WebLogic Server.

The following code example demonstrates securing pathPatterns on the WebSocketListener implementation class, where pathPatterns={"/demo"}:

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Secured WebSocket Endpoint</web-resource-name>
      <url-pattern>/demo/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>user</role-name>
    </auth-constraint>
  </security-constraint>
  <login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
      <form-login-page>/login.jsp</form-login-page>
      <form-error-page>/error.jsp</form-error-page>
    </form-login-config>
  </login-config>
  <security-role>
    <role-name>user</role-name>
  </security-role>

Authorization

You can configure a WebSocket application to authorize certain clients in the accept method of the WebSocketListener implementation class. If the application does not authorize a client to create a WebSocket connection, the server rejects the WebSocket handshake request from that client.

The following code example demonstrates configuring the accept method of the WebSocketListener implementation class:

import weblogic.websocket.WebSocketListener;
import weblogic.websocket.WSHandshakeRequest;
import weblogic.websocket.WSHandshakeResponse;
 
@WebSocket(pathPatterns={"/demo"})
public class MyListener implements WebSocketListener {
    private static final String ORIGIN = "http://www.example.com:7001";
    public boolean accept(WSHandshakeRequest request, WSHandshakeResponse response) {
      return ORIGIN.equals(request.getOrigin()) || hasPermission(request.getUserPrincipal());
    }
 
    private boolean hasPermission(Principal user) {
    // Verifies whether the user is authorized to create this WebSocket connection.
    }
    …
}

Establishing Secure WebSocket Connections

To establish a WebSocket connection, the client sends a handshake request to the server. When using the ws:// URI to open the WebSocket connection, the handshake request is a plain HTTP request; the data being transferred over the established WebSocket connection is not encrypted.

To establish a secure WebSocket connection and prevent data from being intercepted, WebSocket applications should use the wss:// URI. The wss:// URI ensures that clients send handshake requests as HTTPS requests, encrypting transferred data by TLS/SSL.

You can configure a WebSocket application to accept only HTTPS handshake requests, where all WebSocket connections must be encrypted and nonencrypted WebSocket handshake requests are rejected. Specify the <user-data-constraint> element in the web.xml deployment descriptor file of the WebSocket application. For more information about the <user-data-constraint> element, see "web.xml Deployment Descriptor Elements" in Developing Web Applications, Servlets, and JSPs for Oracle WebLogic Server.

The following code example demonstrates configuring the <user-data-constraint> element:

<security-constraint>
    <web-resource-collection>
      <web-resource-name>Secured WebSocket Endpoint</web-resource-name>
      <url-pattern>/demo/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>user</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

Avoiding Mixed Content

If a script attempts to open a WebSockets connection through the ws:// URI (using a plain HTTP request), but the top-level Web page is retrieved through an HTTPS request, the Web page is referred to as mixed content. Although most browsers no longer allow mixed content, some still do. WebSocket applications should avoid mixed content, because it allows certain information that should be protected, such as JSESSIONID and cookies, to be exposed.

For more information about mixed content, see "Web Security Context: User Interface Guidelines" at http://www.w3.org/TR/wsc-ui/#securepage.

Specifying Limits for WebSocket Connections

The WebLogic Server WebSocket API enables you to specify limits for WebSocket connections by configuring attributes of the @WebSocket annotation. By configuring an application to specify limits, you can prevent clients from requesting a large number of connections and potentially exhausting server resources, such as memory, sockets, and so on.

You can specify the following limits:

timeout

The value of the timeout attribute provides the idle timeout value of the WebSocket connection in seconds.

The WebSocket Protocol (RFC 6455) defines Ping and Pong control frames to keep WebSocket connections open. Instead of specifying large values in the timeout attribute, applications should send Ping or Pong messages to keep WebSocket connections open.

maxMessageSize

The value of the maxMessageSize attribute specifies the maximum size of a WebSocket message that the server can receive. The server rejects any message that exceeds this size. If a message is too large, then the client receives a closing frame message with the status code 1009, and the WebSocket connection closes. Applications should specify a reasonable value for this attribute.

For more information about status code 1009, see the WebSocket Protocol (RFC 6455) at http://tools.ietf.org/html/rfc6455.

maxConnections

The value of the maxConnections attribute specifies the maximum number of open connections on this WebSocket. If the number of open connections exceeds this value, then the server rejects any new WebSocket handshake requests. Applications should specify a reasonable value for this attribute.

The following code example demonstrates setting the previous limits:

import weblogic.websocket.WebSocketListener;
import weblogic.websocket.WSHandshakeRequest;
import weblogic.websocket.WSHandshakeResponse;
 
@WebSocket(pathPatterns={"/demo"},
           timeout=15,
           maxMessageSize=1024,
           maxConnections=1000)
public class MyListener implements WebSocketListener {
    …
}

Example of Using WebSockets with WebLogic Server

Example 17-2 uses the WebLogic WebSocket API. In this example, the WebSocket listener receives messages from the browser, encapsulates the received messages, and sends them back to the browser.

Example 17-2 Using WebSockets with WebLogic Server

package examples.webapp.html5.websocket;
 
import weblogic.websocket.ClosingMessage;
import weblogic.websocket.WebSocketAdapter;
import weblogic.websocket.WebSocketConnection;
import weblogic.websocket.annotation.WebSocket;
 
import java.io.IOException;
 
@WebSocket(
  timeout = -1,
  pathPatterns = {"/ws/*"}
)
public class MessageListener extends WebSocketAdapter {
 
  @Override
  public void onMessage(WebSocketConnection connection, String payload) {
    // Sends message from the browser back to the client.
    String msgContent = "Message \"" + payload + "\" has been received by server.";
    try {
      connection.send(msgContent);
    } catch (IOException e) {
      connection.close(ClosingMessage.SC_GOING_AWAY);
    }  
  }
}