Introducing WebSockets

Ibragimov Ruslan, ruslan@ibragimov.by

Agenda

  1. Socket
  2. WebSocket
  3. Как мы жили без них раньше
  4. Применения WebSocket
  5. Стандарты: RFC 6455, JSR 356, W3C WebSocket API
  6. Библиотеки
  7. spring-websocket
  8. STOMP
  9. Security
  10. Масштабирование

Socket?

Socket?

Socket in Java


// On client side
// client socket (TCP or UDP, default TCP)
// @since JDK 1.0
Socket socket = new Socket(hostName, portNumber);

// On server side
// server socket
ServerSocket serverSocket = new ServerSocket(portNumber);
Socket connectedClient = serverSocket.accept();
					

Socket in Java

Socket in Java


// Server logic:
while (true) {
    accept a connection;
    create a thread to deal with the client;
}

// Or in case of NIO:
ServerSocketChannel
					
Alexey Diomin - Need for Speed: Netty & Protobuf (youtube)
A socket is one endpoint of a two-way communication link between two programs running on the network. A socket is bound to a port number so that the TCP layer can identify the application that data is destined to be sent to.
What Is a Socket?

HTTP

An HTTP session is a sequence of network request-response transactions.

Okay

HTTP/1.1

An HTTP/1.1 session is a sequence of network request-response transactions.

HTTP/2

  • ...
  • Decrease latency to improve page load speed in web browsers by considering:
    1. Data compression of HTTP headers
    2. Server push technologies
    3. Fixing the head-of-line blocking problem in HTTP 1
    4. Loading page elements in parallel over a single TCP connection
  • ...
HTTP/2

Polling

Long Polling

HTTP Streaming aka Comet

Problems

  • Low Latency Client-Server and Server-Client Connections
  • Waste

WebSocket

A layer on TCP

Full-duplex, stateful connection

Stream of messages (rather than bytes)

HTTP used for the initial handshake

Handshake


HANDSHAKE REQUEST

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com
					

Handshake Responce


HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
					

Кто уже использовал WebSockets?

Use Cases

  • Multiplayer online games
  • Chat applications
  • Push notifications
  • Realtime updating social streams
  • ...

С какой версии поддерживаются WebSockets в IE?

Can I Use?

caniuse.com/#feat=websockets

В каком году IETF стандартизировала WebSocket протокол как RFC 6455?

Dec-2011

RFC 6455

В каком году был утвержден JSR 356?

May-2013

JSR 356

Part of JavaEE 7

Not using Servlet API

JSR 356

  • Tomcat 8 + backport to Tomcat 7.0.47
  • Jetty 9.1
  • Glassfish 4 with Tyrus WebSocket engine
  • WildFly 8
  • Any Servlet 3.1 container
  • Any JavaEE 7 compatible appserver

Example


@ServerEndpoint("/ws")
public class Getter {

    @OnMessage
    public void getValue(String data, Session client) {
    	String returnText = getTimeStamp();
      	client.getAsyncRemote().sendText(returnText);
    }
}
   					

JSR 356

No fallback

No sub-protocol support

Too low level

Single WebSocket connection per client

results in single @ServerEndpoint per application

Кто пишет приложения на голых сервлетах?

Javascript WebSocket API


var socket = new WebSocket('ws://itx.by:12010/updates');

socket.onopen = function () {
  setInterval(function() {
    if (socket.bufferedAmount == 0)
      socket.send(getUpdateData());
  }, 50);
};

socket.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};

socket.onmessage = function (message) {
  console.log('Server: ' + message.data);
};

socket.onclose = function() {
  console.log('Closed.');
};
					
W3C: The WebSocket API and W3C: Server-Sent Events | MDN: WebSocket

Кто использует spring-context на текущем проекте?

STOMP

Simple protocol for asynchronous message passing

Originally for scripting languages (Ruby, Python)

Supported by message brokers

Suited for use on the web

STOMP Frame Content


COMMAND
header1:value1
header2:value2

body^@
						

Commands


SEND ===>>>
SUBSCRIBE, UNSUBCRIBE ===>>>
MESSAGE <<<===
ERROR <<<===
RECEIPT <<<===
ACK, NACK ===>>>
						

The "Destination" Header

A key concept in STOMP

Opaque string, syntax left to server

Typically URI path-like ("/queue/a", "/topic/a")

Usage

Produce messages: via SEND frame with "destination" header

Consume messages: SUBSCRIBE frame w/ "destination" + MESSAGE frames from server

Server cannot send unsolicited messages!

Example


SEND
destination:/topic/trade
content-type:application/json
content-lenght:46

{"action":"Buy","ticker":"EMC","shares":"44"}^@
						

Configuration


@Configuration
@EnableWebSocketMessageBroker
public class Config implements WebSocketMessageBrokerConfigurer {

  @Override
  public void registerStompEndpoints(StompEndpointRegistry r) {
    r.addEndpoint("/ws").withSockJS(); // WebSocket URL prefix
  }

  @Override
  public void configureMessageBroker(MessageBrokerConfigurer c) {
    c.enableSimpleBroker("/topic/"); // destination prefix
  }
}
						

SockJS Transports by Browser

Recieve message


@Controller
public class TradeController {


  @MessageMapping("/trade")
  @SendTo("/topic/trade")
  public String trade(Trade trade) {

      // Return value broadcast to "/topic/trade"

      return "[" + getTimestamp() + "]: ";
  }


}
						

Send message to users


@RestController
public class TradeController {

  @Autowired
  private SimpMessagingTemplate template;

  @RequestMapping(value="/trade", method=POST)
  public void greet(Trade trade) {
    this.template.convertAndSend("/topic/trade", trade);
  }

}
						

Message Flow

Send message to user


@RestController
public class TradeController {

  // ...

  @MessageExceptionHandler
  @SendToUser("/queue/errors")
  public String handleException(IllegalStateException ex) {
    return ex.getMessage();
  }

}
						

Client side


var socket = new SockJS('/ws');
var client = Stomp.over(socket);

client.connect('', '', function(frame) {

  var user = frame.headers['user-name'];
  var suffix = frame.headers['queue-suffix'];

  client.subscribe("/queue/trade" + suffix, function(msg) {
    // ...
  });
  client.subscribe("/queue/errors" + suffix, function(msg) {
    // ...
  });

}
						

Message Flow

Spring Security 4.0 (Realese soon) and WebSocket


@Configuation
public class WebSocketSecurityConfig extends
          AbstractSecurityWebSocketMessageBrokerConfigurer {
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
      messages
        . simpDestMatchers("/user/queue/errors").permitAll()
        . simpDestMatchers("/**").hasRole("ADMIN");
      }
  }
					

Spring Security 4.0 (Realese soon) and WebSocket


	// or "hasRole('ROLE_ADMIN')"
	@PreAuthorize("hasRole('ADMIN')")
	@MessageMapping("/update")
	public void update(Update update, Principal principal) {
		// ...
	}
					

ws and wss

Performance Testing

Масштабирование

Горизонтальное масштабирование websocket-ов на Ruby

Масштабирование

[1], [2]

Дебажить в Chrome?

Дебажить в Firefox?

Bug 885508

Firebug?

Issue 6330

ibragimov.by / ruslan@ibragimov.by / https://gitter.im/JavaBy/chat