• Stars
    star
    229
  • Rank 168,255 (Top 4 %)
  • Language
    JavaScript
  • Created over 11 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Engine.IO protocol

Engine.IO Protocol

This document describes the 4th version of the Engine.IO protocol.

Table of content

Introduction

The Engine.IO protocol enables full-duplex and low-overhead communication between a client and a server.

It is based on the WebSocket protocol and uses HTTP long-polling as fallback if the WebSocket connection can't be established.

The reference implementation is written in TypeScript:

The Socket.IO protocol is built on top of these foundations, bringing additional features over the communication channel provided by the Engine.IO protocol.

Transports

The connection between an Engine.IO client and an Engine.IO server can be established with:

HTTP long-polling

The HTTP long-polling transport (also simply referred as "polling") consists of successive HTTP requests:

  • long-running GET requests, for receiving data from the server
  • short-running POST requests, for sending data to the server

Request path

The path of the HTTP requests is /engine.io/ by default.

It might be updated by libraries built on top of the protocol (for example, the Socket.IO protocol uses /socket.io/).

Query parameters

The following query parameters are used:

Name Value Description
EIO 4 Mandatory, the version of the protocol.
transport polling Mandatory, the name of the transport.
sid <sid> Mandatory once the session is established, the session identifier.

If a mandatory query parameter is missing, then the server MUST respond with an HTTP 400 error status.

Headers

When sending binary data, the sender (client or server) MUST include a Content-Type: application/octet-stream header.

Without an explicit Content-Type header, the receiver SHOULD infer that the data is plaintext.

Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type

Sending and receiving data

Sending data

To send some packets, a client MUST create an HTTP POST request with the packets encoded in the request body:

CLIENT                                                 SERVER

  β”‚                                                      β”‚
  β”‚   POST /engine.io/?EIO=4&transport=polling&sid=...   β”‚
  β”‚ ───────────────────────────────────────────────────► β”‚
  β”‚ β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
  β”‚                        HTTP 200                      β”‚
  β”‚                                                      β”‚

The server MUST return an HTTP 400 response if the session ID (from the sid query parameter) is not known.

To indicate success, the server MUST return an HTTP 200 response, with the string ok in the response body.

To ensure packet ordering, a client MUST NOT have more than one active POST request. Should it happen, the server MUST return an HTTP 400 error status and close the session.

Receiving data

To receive some packets, a client MUST create an HTTP GET request:

CLIENT                                                SERVER

  β”‚   GET /engine.io/?EIO=4&transport=polling&sid=...   β”‚
  β”‚ ──────────────────────────────────────────────────► β”‚
  β”‚                                                   . β”‚
  β”‚                                                   . β”‚
  β”‚                                                   . β”‚
  β”‚                                                   . β”‚
  β”‚ β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
  β”‚                       HTTP 200                      β”‚

The server MUST return an HTTP 400 response if the session ID (from the sid query parameter) is not known.

The server MAY not respond right away if there are no packets buffered for the given session. Once there are some packets to be sent, the server SHOULD encode them (see Packet encoding) and send them in the response body of the HTTP request.

To ensure packet ordering, a client MUST NOT have more than one active GET request. Should it happen, the server MUST return an HTTP 400 error status and close the session.

WebSocket

The WebSocket transport consists of a WebSocket connection, which provides a bidirectional and low-latency communication channel between the server and the client.

The following query parameters are used:

Name Value Description
EIO 4 Mandatory, the version of the protocol.
transport websocket Mandatory, the name of the transport.
sid <sid> Optional, depending on whether it's an upgrade from HTTP long-polling or not.

If a mandatory query parameter is missing, then the server MUST close the WebSocket connection.

Each packet (read or write) is sent its own WebSocket frame.

A client MUST NOT open more than one WebSocket connection per session. Should it happen, the server MUST close the WebSocket connection.

Protocol

An Engine.IO packet consists of:

  • a packet type
  • an optional packet payload

Here is the list of available packet types:

Type ID Usage
open 0 Used during the handshake.
close 1 Used to indicate that a transport can be closed.
ping 2 Used in the heartbeat mechanism.
pong 3 Used in the heartbeat mechanism.
message 4 Used to send a payload to the other side.
upgrade 5 Used during the upgrade process.
noop 6 Used during the upgrade process.

Handshake

To establish a connection, the client MUST send an HTTP GET request to the server:

  • HTTP long-polling first (by default)
CLIENT                                                    SERVER

  β”‚                                                          β”‚
  β”‚        GET /engine.io/?EIO=4&transport=polling           β”‚
  β”‚ ───────────────────────────────────────────────────────► β”‚
  β”‚ β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
  β”‚                        HTTP 200                          β”‚
  β”‚                                                          β”‚
  • WebSocket-only session
CLIENT                                                    SERVER

  β”‚                                                          β”‚
  β”‚        GET /engine.io/?EIO=4&transport=websocket         β”‚
  β”‚ ───────────────────────────────────────────────────────► β”‚
  β”‚ β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
  β”‚                        HTTP 101                          β”‚
  β”‚                                                          β”‚

If the server accepts the connection, then it MUST respond with an open packet with the following JSON-encoded payload:

Key Type Description
sid string The session ID.
upgrades string[] The list of available transport upgrades.
pingInterval number The ping interval, used in the heartbeat mechanism (in milliseconds).
pingTimeout number The ping timeout, used in the heartbeat mechanism (in milliseconds).
maxPayload number The maximum number of bytes per chunk, used by the client to aggregate packets into payloads.

Example:

{
  "sid": "lv_VI97HAXpY6yYWAAAC",
  "upgrades": ["websocket"],
  "pingInterval": 25000,
  "pingTimeout": 20000,
  "maxPayload": 1000000
}

The client MUST send the sid value in the query parameters of all subsequent requests.

Heartbeat

Once the handshake is completed, a heartbeat mechanism is started to check the liveness of the connection:

CLIENT                                                 SERVER

  β”‚                   *** Handshake ***                  β”‚
  β”‚                                                      β”‚
  β”‚  ◄─────────────────────────────────────────────────  β”‚
  β”‚                           2                          β”‚  (ping packet)
  β”‚  ─────────────────────────────────────────────────►  β”‚
  β”‚                           3                          β”‚  (pong packet)

At a given interval (the pingInterval value sent in the handshake) the server sends a ping packet and the client has a few seconds (the pingTimeout value) to send a pong packet back.

If the server does not receive a pong packet back, then it SHOULD consider that the connection is closed.

Conversely, if the client does not receive a pong packet within pingInterval + pingTimeout, then it SHOULD consider that the connection is closed.

Upgrade

By default, the client SHOULD create an HTTP long-polling connection, and then upgrade to better transports if available.

To upgrade to WebSocket, the client MUST:

  • pause the HTTP long-polling transport (no more HTTP request gets sent), to ensure that no packet gets lost
  • open a WebSocket connection with the same session ID
  • send a ping packet with the string probe in the payload

The server MUST:

  • send a noop packet to any pending GET request (if applicable) to cleanly close HTTP long-polling transport
  • respond with a pong packet with the string probe in the payload

Finally, the client MUST send a upgrade packet to complete the upgrade:

CLIENT                                                 SERVER

  β”‚                                                      β”‚
  β”‚   GET /engine.io/?EIO=4&transport=websocket&sid=...  β”‚
  β”‚ ───────────────────────────────────────────────────► β”‚
  β”‚  β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
  β”‚            HTTP 101 (WebSocket handshake)            β”‚
  β”‚                                                      β”‚
  β”‚            -----  WebSocket frames -----             β”‚
  β”‚  ─────────────────────────────────────────────────►  β”‚
  β”‚                         2probe                       β”‚ (ping packet)
  β”‚  ◄─────────────────────────────────────────────────  β”‚
  β”‚                         3probe                       β”‚ (pong packet)
  β”‚  ─────────────────────────────────────────────────►  β”‚
  β”‚                         5                            β”‚ (upgrade packet)
  β”‚                                                      β”‚

Message

Once the handshake is completed, the client and the server can exchange data by including it in a message packet.

Packet encoding

The serialization of an Engine.IO packet depends on the type of the payload (plaintext or binary) and on the transport.

HTTP long-polling

Due to the nature of the HTTP long-polling transport, multiple packets might be concatenated in a single payload in order to increase throughput.

Format:

<packet type>[<data>]<separator><packet type>[<data>]<separator><packet type>[<data>][...]

Example:

4hello\x1e2\x1e4world

with:

4      => message packet type
hello  => message payload
\x1e   => separator
2      => ping packet type
\x1e   => separator
4      => message packet type
world  => message payload

The packets are separated by the record separator character: \x1e

Binary payloads MUST be base64-encoded and prefixed with a b character:

Example:

4hello\x1ebAQIDBA==

with:

4         => message packet type
hello     => message payload
\x1e      => separator
b         => binary prefix
AQIDBA==  => buffer <01 02 03 04> encoded as base64

The client SHOULD use the maxPayload value sent during the handshake to decide how many packets should be concatenated.

WebSocket

Each Engine.IO packet is sent in its own WebSocket frame.

Format:

<packet type>[<data>]

Example:

4hello

with:

4      => message packet type
hello  => message payload (UTF-8 encoded)

Binary payloads are sent as is, without modification.

History

From v2 to v3

  • add support for binary data

The 2nd version of the protocol is used in Socket.IO v0.9 and below.

The 3rd version of the protocol is used in Socket.IO v1 and v2.

From v3 to v4

  • reverse ping/pong mechanism

The ping packets are now sent by the server, because the timers set in the browsers are not reliable enough. We suspect that a lot of timeout problems came from timers being delayed on the client-side.

  • always use base64 when encoding a payload with binary data

This change allows to treat all payloads (with or without binary) the same way, without having to take in account whether the client or the current transport supports binary data or not.

Please note that this only applies to HTTP long-polling. Binary data is sent in WebSocket frames with no additional transformation.

  • use a record separator (\x1e) instead of counting of characters

Counting characters prevented (or at least makes harder) to implement the protocol in other languages, which may not use the UTF-16 encoding.

For example, € was encoded to 2:4€, though Buffer.byteLength('€') === 3.

Note: this assumes the record separator is not used in the data.

The 4th version (current) is included in Socket.IO v3 and above.

Test suite

The test suite in the test-suite/ directory lets you check the compliance of a server implementation.

Usage:

  • in Node.js: npm ci && npm test
  • in a browser: simply open the index.html file in your browser

For reference, here is expected configuration for the JavaScript server to pass all tests:

import { listen } from "engine.io";

const server = listen(3000, {
  pingInterval: 300,
  pingTimeout: 200,
  maxPayload: 1e6,
  cors: {
    origin: "*"
  }
});

server.on("connection", socket => {
  socket.on("data", (...args) => {
    socket.send(...args);
  });
});

More Repositories

1

socket.io

Realtime application framework (Node.JS server)
TypeScript
59,936
star
2

socket.io-client

Realtime application framework (client)
TypeScript
10,492
star
3

socket.io-client-java

Full-featured Socket.IO Client Library for Java, which is compatible with Socket.IO v1.0 and later.
Java
5,244
star
4

socket.io-client-swift

Swift
5,128
star
5

engine.io

The engine used in the Socket.IO JavaScript server, which manages the low-level transports such as HTTP long-polling and WebSocket.
JavaScript
4,569
star
6

socket.io-redis-adapter

Adapter to enable broadcasting of events to multiple separate socket.io server nodes.
TypeScript
2,712
star
7

socket.io-client-cpp

C++11 implementation of Socket.IO client
C++
2,181
star
8

socket.io-p2p

JavaScript
1,022
star
9

engine.io-client

The engine used in the Socket.IO JavaScript client, which manages the low-level transports such as HTTP long-polling, WebSocket and WebTransport.
JavaScript
738
star
10

socket.io-redis-emitter

The Socket.IO Redis emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process.
TypeScript
712
star
11

socket.io-protocol

Socket.IO Protocol specification
JavaScript
494
star
12

engine.io-client-java

Engine.IO Client Library for Java
Java
356
star
13

socket.io-admin-ui

Admin UI for Socket.IO
Vue
322
star
14

socket.io-website

Socket.IO website and blog
JavaScript
311
star
15

socket.io-adapter

The Socket.IO in-memory adapter
TypeScript
195
star
16

engine.io-server-java

Engine.IO Server Library for Java
Java
161
star
17

socket.io-parser

JavaScript
133
star
18

socket.io-deno

Socket.IO server for Deno
TypeScript
88
star
19

engine.io-parser

Parser for the engine.io protocol, used by client and server
TypeScript
75
star
20

socket.io-msgpack-parser

Socket.IO parser based on msgpack
JavaScript
62
star
21

socket.io-sticky

A simple and performant way to use Socket.IO within a cluster.
JavaScript
37
star
22

socket.io-mongo-adapter

The Socket.IO MongoDB adapter, allowing to broadcast events between several Socket.IO servers
TypeScript
21
star
23

socket.io-chat-platform

A basic chat platform based on Socket.IO
JavaScript
19
star
24

socket.io-postgres-adapter

The Socket.IO Postgres adapter, allowing to broadcast events between several Socket.IO servers
TypeScript
18
star
25

socket.io-redis-streams-adapter

The Socket.IO adapter based on Redis Streams, allowing to broadcast events between several Socket.IO servers.
TypeScript
17
star
26

socket.io-cluster-adapter

The Socket.IO official cluster adapter, allowing to broadcast events between several Socket.IO servers.
TypeScript
14
star
27

socket.io-json-parser

socket.io parser based on JSON.stringify / JSON.parse
JavaScript
12
star
28

socket.io-minimal-example

Socket.IO minimal example
HTML
8
star
29

socket.io-benchmarks

Benchmarks for Socket.IO
JavaScript
8
star
30

socket.io-compression-demo

JavaScript
7
star
31

get-started-drawing

7
star
32

lz77-compression-demo

JavaScript
7
star
33

socket.io-mongo-emitter

The Socket.IO MongoDB emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process
TypeScript
6
star
34

socket.io-postgres-emitter

The Socket.IO Postgres emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process.
TypeScript
5
star
35

socket.io-echo-server

Socket.IO echo server
JavaScript
4
star
36

socket.io-sample-playbook

This repository contains an Ansible playbook to set up a basic Socket.IO application.
JavaScript
4
star
37

tap-to-android

3
star
38

socket.io-swift-fiddle

Swift
2
star
39

socket.io-browsers

A reusable list of browsers to test when using defunctzombie/zuul
JavaScript
1
star