WebSocket


The WebSocket Protocol


1. Introduction

1.1. Background


HTTP was not initially meant to be used for bidirectional communication.
A simpler solution would be to use a single TCP connection for traffic in both directions. This is what the WebSocket Protocol provides.

1.2. Protocol Overview


The WebSocket Protocol attempts to address the goals of existing bidirectional HTTP technologies in the context of the existing HTTP infrastructure.
  • HTTP Request message
  • 
      Method SP Request-URI SP HTTP-Version CRLF
      *(( general-header 
      | request-header
      | entity-header ) CRLF)
      CRLF
      [ message-body ]
    
    request-header =
    
                          Accept
                          | Accept-Charset
                          | Accept-Encoding
                          | Accept-Language
                          | Authorization 
                          | Expect
                          | From 
                          | Host 
                          | If-Match   
    
  • HTTP Response message
  • 
      HTTP-Version SP Status-Code SP Reason-Phrase CRLF
      *(( general-header 
      | response-header
      | entity-header ) CRLF)
      CRLF
      [ message-body ]
    
    Status-Code =
    
                "100"  ;  Continue
              | "101"  ;  Switching Protocols
              | "200"  ;  OK
              | "201"  ;  Created
              | "202"  ;  Accepted
              | "203"  ;  Non-Authoritative Information
              | "204"  ;  No Content
              | "205"  ;  Reset Content
              | "206"  ;  Partial Content
              | "300"  ;  Multiple Choices
              | "301"  ;  Moved Permanently
              | "302"  ;  Found
              | "303"  ;  See Other
              | "304"  ;  Not Modified
              | "305"  ;  Use Proxy
              | "307"  ;  Temporary Redirect
              | "400"  ;  Bad Request
              | "401"  ;  Unauthorized
              | "402"  ;  Payment Required
              | "403"  ;  Forbidden
              | "404"  ;  Not Found
              | "405"  ;  Method Not Allowed
              | "406"  ;  Not Acceptable
              | "407"  ;  Proxy Authentication Required
              | "408"  ;  Request Time-out
              | "409"  ;  Conflict
              | "410"  ;  Gone
              | "411"  ;  Length Required
              | "412"  ;  Precondition Failed
              | "413"  ;  Request Entity Too Large
              | "414"  ;  Request-URI Too Large
              | "415"  ;  Unsupported Media Type
              | "416"  ;  Requested range not satisfiable
              | "417"  ;  Expectation Failed
              | "500"  ;  Internal Server Error
              | "501"  ;  Not Implemented
              | "502"  ;  Bad Gateway
              | "503"  ;  Service Unavailable
              | "504"  ;  Gateway Time-out
              | "505"  ;  HTTP Version not supported
              | extension-code
    
    response-header =
    
                           Accept-Ranges           
                           | Age                    
                           | ETag              
                           | Location
                           | Proxy-Authenticate
                           | Retry-After   
                           | Server     
                           | Vary    
                           | WWW-Authenticate
    
general-header =

                      Cache-Control 
                      | Connection 
                      | Date  
                      | Pragma  
                      | Trailer
                      | Transfer-Encoding 
                      | Upgrade
                      | Via  
                      | Warning 

The protocol has two parts: a handshake and the data transfer.


3. WebSocket URIs

  • ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
  • wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]

4. Opening Handshake


4.1. Client Requirements


Once a connection to the server has been established, the client MUST send an opening handshake to the server. The handshake consists of an HTTP Upgrade request, along with a list of required and optional header fields.

GET uri HTTP/1.1
Host:
Upgrade:websocket
Connection: Upgrade
Sec-WebSocket-Key:nonce
Origin:http://www.example.com               
Sec-WebSocket-Version:13
Sec-WebSocket-Protocol:
Sec-WebSocket-Extensions:
  • Origin
  • The Origin request header indicates where a fetch originates from. It doesn't include any path information, but only the server name. Two objects have the same origin only when the scheme, host, and port all match.

Once the client's opening handshake has been sent, the client MUST wait for a response from the server before sending any further data. The client MUST validate the server's response.

4.2. Server-Side Requirements


4.2.1. Reading the Client's Opening Handshake

4.2.2. Sending the Server's Opening Handshake


  • If the connection is happening on an HTTPS (HTTP-over-TLS) port, perform a TLS handshake over the connection.
  • If this fails, then close the connection; otherwise, all further communication for the connection (including the server's handshake) MUST run through the encrypted tunnel
  • The server can perform additional client authentication
  • For example, by returning a 401 status code with the corresponding WWW-Authenticate header field.
  • The server MAY redirect the client using a 3xx status code
  • If the server chooses to accept the incoming connection, it MUST reply with a valid HTTP response indicating the following.
  • 
    HTTP/1.1 101 Switching Protocols           
    Upgrade:websocket
    Connection: Upgrade
    Sec-WebSocket-Acceptsecret-value
    Sec-WebSocket-Protocol:
    Sec-WebSocket-Extensions:
    
    • Sec-WebSocket-Accept
    • The value of this header field is constructed by concatenating the Sec-WebSocket-Key header field in the client's handshake with "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" then generates the SHA-1 hash as the secret value.
If the server finishes these steps without aborting the WebSocket handshake, the server considers the WebSocket connection to be established and that the WebSocket connection is in the OPEN state. At this point, the server may begin sending (and receiving) data.

WebSocket: LIGHTWEIGHT CLIENT-SERVER COMMUNICATIONS

by
Andrew Lombardi

CHAPTER 1 Quick Start


source of the examples

WebSocket gives you a bidirectional, full-duplex communications channel that operates over HTTP through a single socket.

Getting Node and npm


  • Installing on OS X
  • 
       /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
       brew -v
       brew install node
       node -v
       npm -v
    
  • Installing on Linux
  • 
        sudo apt-get update
        sudo apt-get install nodejs
        sudo apt install npm
    
NPM is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency conflicts intelligently. It is a package repository service that hosts published JavaScript modules.
npm install is a command that lets you download packages from their repository.
The npm cli puts all the downloaded modules in a node_modules directory where you ran npm install. Node.js has very detailed documentation on how modules find other modules which includes finding a node_modules directory.

Hello, World! Example


The ws library can take a lot of the headache out of writing a WebSocket server (or client) by offering a simple, clean API for your Node.js application. Install it using npm:

 npm install ws
  • server
  • server.js:
    
    var WebSocketServer = require('ws').Server;
    var wss = new WebSocketServer({port: 8181});
    
    wss.on('connection', function(ws) { 
             console.log('client connected'); 
             ws.on('message', function(message) {
                console.log(message);
            });
    });
    
    run the server so it’s listening for the client you’re about to code:
    
        node server.js
    
  • client
  • client.html:
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <title>WebSocket Echo Demo</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
    <script>
    var ws = new WebSocket("ws://localhost:8181");
    ws.onopen = function(e) {
      console.log('Connection to server opened');
    }
    function sendMessage() {
      ws.send($('#message').val());
    }
    </script>
    </head>
    <body lang="en">
    <div class="vertical-center">
    <div class="container">
    <p> </p>
    <form role="form" id="chat_form" onsubmit="sendMessage(); return false;">
    <div class="form-group">
    <input class="form-control" type="text" name="message" id="message"
    placeholder="Type text to echo in here" value="" autofocus/>
    </div>
    <button type="button" id="send" class="btn btn-primary"
    onclick="sendMessage();">Send!</button>
    </form>
    </div>
    </div>
    <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js>
    </body>
    </html>
    
    

Why WebSocket?


WebSocket gives you the ability to use an upgraded HTTP request, and send data in a message-based way, similar to UDP and with all
the reliability of TCP. You can also layer another protocol on top of WebSocket, and provide it in a secure way over TLS.


CHAPTER 2 WebSocket API



WebSocket is an event-driven, full-duplex asynchronous communications channel for your web applications.

While WebSocket uses HTTP as the initial transport mechanism, the communication doesn’t end after a response is received by the client., as long as the connection stays open, the client and server can freely send messages asynchronously without polling for anything new.

Initializing


Create a WebSocket object called ws that you can use to listen for events:

  var ws = new WebSocket( url );
url can be a string started with "ws://" or "wss://" (if using TLS).

Stock Example UI



<!DOCTYPE html>
<html lang="en">
<head>
<title>Stock Chart over WebSocket</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<style type="text/css">
html, body {
  height: 100%;
}
</style>

<script>

$(function() {

    var ws = new WebSocket("ws://localhost:8181");

    var stock_request = {
        "stocks": ["AAPL", "MSFT", "AMZN", "GOOG", "YHOO"]
    };

    var stocks = {
        "AAPL": 0,
        "MSFT": 0,
        "AMZN": 0,
        "GOOG": 0,
        "YHOO": 0
    }

    $('#AAPL span').toggleClass('label-success');
    ws.onopen = function(e) {
      console.log('Connection to server opened');
      ws.send(JSON.stringify(stock_request));
    }

    var changeStockEntry = function(symbol, originalValue, newValue) {
        var valElem = $('#' + symbol + ' span');
        valElem.html(newValue.toFixed(2));
        if(newValue < originalValue) {
            valElem.addClass('label-danger');
            valElem.removeClass('label-success');
        } else if(newValue > originalValue) {
            valElem.addClass('label-success');
            valElem.removeClass('label-danger');
        }
    }

    ws.onmessage = function(e) {
      var stocksData = JSON.parse(e.data);
      for(var symbol in stocksData) {
          if(stocksData.hasOwnProperty(symbol)) {
              changeStockEntry(symbol, stocksData[symbol], stocks[symbol]);
              stocks[symbol] = stocksData[symbol];
          }
      }
    }
    ws.onclose = function(e) {
      console.log("Connection closed");
      for(var symbol in stocks) {
          if(stocks.hasOwnProperty(symbol)) {
              stocks[symbol] = 0;
          }
      }

    }

    function disconnect() {
      ws.close();
    }
});

</script>

</head>
<body lang="en">
    <div class="vertical-center">
        <div class="container">

            <h1>Stock Chart over WebSocket</h1>
            <table class="table" id="stockTable">
              <thead>
                <tr>
                  <th>Symbol</th>
                  <th>Price</th>
                </tr>
              </thead>
              <tbody id="stockRows">
                <tr>
                  <td><h3>AAPL</h3></td>
                  <td id="AAPL"><h3><span class="label label-default">95.00</span></h3></td>
                </tr>
                <tr>
                  <td><h3>MSFT</h3></td>
                  <td id="MSFT"><h3><span class="label label-default">50.00</span></h3></td>
                </tr>
                <tr>
                  <td><h3>AMZN</h3></td>
                  <td id="AMZN"><h3><span class="label label-default">300.00</span></h3></td>
                </tr>
                <tr>
                  <td><h3>GOOG</h3></td>
                  <td id="GOOG"><h3><span class="label label-default">550.00</span></h3></td>
                </tr>
                <tr>
                  <td><h3>YHOO</h3></td>
                  <td id="YHOO"><h3><span class="label label-default">35.00</span></h3></td>
                </tr>
              </tbody>
            </table>

          </div>
    </div>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body></html>


WebSocket Events


The API for WebSocket is based around events.
Listen to these events using addEventListener() or by assigning an event listener to the oneventname property of this interface.
The WebSocket object provides properties related to events:
  • WebSocket.onclose
  • An event listener to be called when the connection is closed.
    
    ws.onclose = function(e) {
      console.log(e.reason + " " + e.code);
      for(var symbol in stocks) {
        if(stocks.hasOwnProperty(symbol)) {
          stocks[symbol] = 0;
        }
      }
    };
    
    ws.close(1000, 'WebSocket connection closed')
    
    
  • WebSocket.onopen
  • An event listener to be called when the connection is opened.
    
    var stock_request = {"stocks": ["AAPL", "MSFT", "AMZN", "GOOG", "YHOO"]};
    var stocks = {"AAPL": 0,"MSFT": 0,"AMZN": 0,"GOOG": 0,"YHOO":0};
    
    // WebSocket connection established
    ws.onopen = function(e) {
      console.log("Connection established");
      ws.send(JSON.stringify(stock_request));
    };
    
    After the open event is fired, it is ready to send and receive messages from your client/server application
  • WebSocket.onerror
  • An event listener to be called when an error occurs.
    
    ws.onerror = function(e) {
      console.log("WebSocket failure, error", e);
      handleErrors(e);
    };
    
  • WebSocket.onmessage
  • An event listener to be called when a message is received from the server.
    
    // UI update function
    var changeStockEntry =  function (symbol, originalValue, newValue)  { 
      var valElem = $('#' + symbol + ' span');
      valElem.html(newValue.toFixed(2));
      if(newValue < originalValue) {
        valElem.addClass('label-danger');
        valElem.removeClass('label-success');
      } else if(newValue > originalValue) {
        valElem.addClass('label-success');
        valElem.removeClass('label-danger');
      }
     };
    
    // WebSocket message handler
    ws.onmessage = function(e) {
      var stocksData = JSON.parse(e.data);
      for(var symbol in stocksData) {
        if(stocksData.hasOwnProperty(symbol)) {
          changeStockEntry(symbol, stocks[symbol], stocksData[symbol]);
          stocks[symbol] = stocksData[symbol];
        }
      }
    };
    

WebSocket Methods


The creators of WebSocket kept its methods pretty simple—there are only two: send() and close() .
  • Send
  • You need to ensure that the connection is open and ready to receive messages before you send the message.
  • Close
  • After this method is called, no more data can be sent or received from this connection. Optionally, you can pass a numeric code and a human-readable reason through the close() method.
    
    // Close the WebSocket connection with reason.
    ws.close(1000, "Goodbye, World!");
    

WebSocket Attributes


When the event for open is fired, the WebSocket object can have several possible attributes that can be read.
  • readyState
  • The value of readyState:
    • WebSocket.CONNECTING
    • The connection is not yet open.
    • WebSocket.OPEN
    • The connection is open and ready to communicate.
    • WebSocket.CLOSING
    • The connection is in the process of closing.
    • WebSocket.CLOSED
    • The connection is closed or couldn’t be opened.
  • bufferedAmount
  • The amount of data buffered for sending. Use of the bufferedAmount attribute can be useful for ensuring that all data is sent before closing a connection.
  • protocol
  • The optional protocol argument used in the constructor for WebSocket,

Stock Example Server




const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8181 });

var stocks = {
  "AAPL":95.0,
  "MSFT":50.0,
  "AMZN":300.0,
  "GOOG":550.0,
  "YHOO":35.0
}

function randomInterval(min, max) {
  return Math.floor(Math.random()*(max-min+1)+min);
}

var stockUpdater;
var randomStockUpdater = function() {
  for (var symbol in stocks) {
    if(stocks.hasOwnProperty(symbol)) {
      var randomizedChange = randomInterval(-150, 150);
      var floatChange = randomizedChange / 100;
      stocks[symbol] += floatChange;
    }
  }
  // get a random period between 0.5s ~ 2.5s
  var randomMSTime = randomInterval(500, 2500);
  stockUpdater = setTimeout(
    function() {
      randomStockUpdater();
    }, 
    randomMSTime
  )
}

// start the timer which calls itself repeatedly
randomStockUpdater(); 

wss.on('connection', function(ws) {
  var clientStockUpdater;
  var sendStockUpdates = function(ws) {
    if(ws.readyState == 1) {
      var stocksObj = {};
      for(var i=0; i < clientStocks.length; i++) {
        symbol = clientStocks[i];
        stocksObj[symbol] = stocks[symbol];
      }
      ws.send(JSON.stringify(stocksObj));
    }
  }

  clientStockUpdater = setInterval( function() {
    sendStockUpdates(ws);
    }
    , 1000);
  
  var clientStocks = [];
  ws.on('message', function(message) {
    var stock_request = JSON.parse(message);
    clientStocks = stock_request['stocks'];
    sendStockUpdates(ws);
  });
  
  ws.on('close', function() {
    if(typeof clientStockUpdater !== 'undefined') {
      clearInterval(clientStockUpdater);
    }
  });
}
);


  • require('ws')
  • require() is not part of the standard JavaScript API. But in Node.js, it's a built-in function with a special purpose: to load modules. Modules are a way to split an application into separate files instead of having all of your application in one file. This concept is also present in other languages with minor differences in syntax and behavior, like C's include, Python's import, and so on. One big difference between Node.js modules and browser JavaScript is how one script's code is accessed from another script's code:
    • In browser JavaScript, scripts are added via the <script> element. When they execute, they all have direct access to the global scope, a "shared space" among all scripts. Any script can freely define/modify/remove/call anything on the global scope.
    • In Node.js, each module has its own scope. A module cannot directly access things defined in another module unless it chooses to expose them. To expose things from a module, they must be assigned to exports or module.exports. For a module to access another module's exports or module.exports, it must use require().
    After the module is loaded, you can access functionality of the module's APIs via the variable. require() is specific to Node.js. Browsers do not implement this module system. 'ws' is a tested WebSocket client and server implementation. This module does not work in the browser.
  • WebSocket.Server
  • This class represents a WebSocket server. It extends the EventEmitter to provide the server APIs.
    • new WebSocket.Server(options[, callback])
    • options:
      • host
      • String. The hostname where to bind the server.
      • port
      • Number. The port where to bind the server.
      • verifyClient
      • Function. A function which can be used to validate incoming connections. If verifyClient is not set then the handshake is automatically accepted. If it is provided with a single argument then that is: info:
        • origin
        • String. The value in the Origin header indicated by the client.
        • req
        • http.IncomingMessage. The client HTTP GET request.
        • secure
        • Boolean. true if req.connection.authorized or req.connection.encrypted is set.
        if verifyClient is provided with two arguments then those are:
        • info
        • Object. Same as above.
        • cb
        • Function} A callback that must be called by the user upon inspection of the info fields. Arguments in this callback are:
          • result
          • Boolean. Whether or not to accept the handshake.
          • code
          • Number. When result is false this field determines the HTTP error status code to be sent to the client.
          • name
          • String. When result is false this field determines the HTTP reason phrase.
          • headers
          • Object. When result is false this field determines additional HTTP headers to be sent to the client. For example, { 'Retry-After': 120 }.
        
        
        
    • Math.random()
    • 隨機產生出0~1之間的小數
    • Math.floor()
    • 無條件捨去後的最大整數
    • object.hasOwnProperty(propname)
    • Check if object has a noninherited property with the name specified by propname. The hasOwnProperty() method provides a way to distinguish between inherited properties and noninherited local properties.
    • setTimeout(code[, delay])
    • This sets a timer which executes a function or specified piece of code once the timer expires.
    • setInterval(code[, delay])
    • This allows us to run a function repeatedly, starting after the interval of time, then repeating continuously at that interval.
    • clearInterval()
    • The clearInterval() method clears a timer set with the setInterval() method. The ID value returned by setInterval() is used as the parameter for the clearInterval() method.

    CHAPTER 3 Bidirectional Chat


    You’ll use npm to install a Node module "uuid" for generating a UUID:
    
    npm install uuid
    

    server.js



    
    var WebSocket = require('ws');
    var WebSocketServer = WebSocket.Server,
        wss = new WebSocketServer({port: 8181,
            verifyClient: function(info, callback) {
                console.log(info.req);
                if(info.origin === 'file://') {
                    callback(true);
                    return;
                }
                callback(false);
            }
        });
    var uuid = require('uuid/v4');
    
    var clients = [];
    
    wss.on('connection', function(ws) {
      var client_uuid = uuid();
      clients.push({"id": client_uuid, "ws": ws});
      console.log('client [%s] connected', client_uuid);
      ws.on('message', function(message) {
        for (var i=0; i < clients.length; i++) {
            var clientSocket = clients[i].ws;
            if(clientSocket.readyState === WebSocket.OPEN) {
                console.log('client [%s]: %s', clients[i].id, message);
                clientSocket.send(JSON.stringify({
                    "id": client_uuid,
                    "message": message
                }));
            }
        }
      });
    
      ws.on('close', function() {
        for(var i=0; i<clients.length; i++) {
            if(clients[i].id == client_uuid) {
                console.log('client [%s] disconnected', client_uuid);
                clients.splice(i, 1);
            }
        }
      });
    });
    
    The server that will accept connections from WebSocket clients, and will rebroadcast received messages to all connected clients.

    Client.html

    
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <title>Bi-directional WebSocket Chat Demo</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script>
            var ws = new WebSocket("ws://localhost:8181");
            ws.onopen = function(e) {
              console.log('Connection to server opened');
            }
    
            function appendLog(message) {
              var messages =  document.getElementById('messages');
              var messageElem = document.createElement("li");
    
              messageElem.innerHTML = "<h2><span class=\"label label-success\">*</span>  " + message + "</h2>";
              messages.appendChild(messageElem);
            }
    
            ws.onmessage = function(e) {
              var data = JSON.parse(e.data);
              appendLog(data.message);
              console.log("ID: [%s] = %s", data.id, data.message);
            }
    
            ws.onclose = function(e) {
              appendLog("Connection closed");
              console.log("Connection closed");
            }
    
            function sendMessage() {
                if(ws.readyState === WebSocket.OPEN) {
                    ws.send($('#message').val());
                }
                $('#message').val('');
                $('#message').focus();
            }
    
            function disconnect() {
              ws.close();
            }
        </script>
    </head>
    <body lang="en">
        <div class="vertical-center">
        <div class="container">
        <ul id="messages" class="list-unstyled">
    
        </ul>
        <hr />
        <form role="form" id="chat_form" onsubmit="sendMessage(); return false;">
            <div class="form-group">
            <input class="form-control" type="text" name="message" id="message"
              placeholder="Type text to echo in here" value="" autofocus/>
          </div>
            <button type="button" id="send" class="btn btn-primary" onclick="sendMessage();">Send Message</button>
              <!--
            <input type="button" value="Disconnect" onclick="disconnect();" />
        -->
        </form>
        </div>
        </div>
    <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
    </body>
    </html>
    
    
    The content of an HTML element can be accessed by the innerHTML property of that element.

    CHAPTER 4 STOMP over WebSocket


    CHAPTER 5 WebSocket Compatibility


    CHAPTER 6 WebSocket Security


    TLS and WebSocket


    Generating a Self-Signed Certificate


    • CA
      • Generate the root private key
      • 
        openssl genrsa -aes256 -out private/cakey.pem 4096
        
      • Create the root certificate
      • 
        openssl req -new -x509 -key private/cakey.pem -out cacert.pem -days 3650 -set_serial 0
        
    • CO Server
      • Generate the CO server key
      • 
        openssl genrsa -aes256 -out co_key.pem 2048
        
      • Generate the CO server's CSR (Certificate Signing Request)
      • 
        openssl req -new -key co_key.pem -out co.csr
        
        Remove the the pass-phrase from the key:
        
        cp co_key.pem co_key.passed.pem
        openssl rsa -in co_key.passed.pem -out co_key.pem
        
      • Create the CO server certificate
      • 
        openssl ca -in co.csr -out co.pem
        
    • Web client
      • Generate the Web client key
      • 
        openssl genrsa -aes256 -out web_key.pem 2048
        
      • Generate the Web client private key
      • 
        openssl req -new -key web_key.pem -out web.csr
        
      • Create the Web client certificate
      • 
        openssl ca -in web.csr -out web.pem
        
    Files *.csr are used for sending to a Certificate Authority for a validated certificate.

    Creating a very simple HTTPS server


    
    var fs = require("fs"), https = require("https");
    var privateKey = fs.readFileSync('/home/jerry/ca/requests/co_key.pem').toString();
    var certificate = fs.readFileSync('/home/jerry/ca/requests/co.pem').toString();
    var options = {
        key: privateKey,
        cert: certificate
    };
    
    https.createServer(options, function(req,res) {
        res.writeHead(200);
        res.end("Hello Secure World\n");
        }
        ).listen(3000);
    
    Browse https://192.168.122.1:3000/ to get the message "Hello Secure World" returned.

    Setting up WebSocket over TLS: Example


    An example of using the https module to allow the bidirectional WebSocket communication to happen over TLS and listen on port 8080:
    
    var fs = require('fs');
    var https = require('https');
    var http = require('http');
    var WebSocket = require('ws');
    
    // you'll probably load configuration from config
    var cfg = {
        ssl: true,
        host: '0.0.0.0',
        port: 8080,
        ssl_key: '/home/jerry/ca/private/co_key.pem',
        ssl_cert: '/home/jerry/ca/certs/co.pem'
    };
    
    var WebSocketServer = WebSocket.Server;
    var app = null;
    
    fs.readFile('./index.html', function(err, html) {
        if(err) {
            throw err;
        }
        app = https.createServer(
               {
                key: fs.readFileSync( cfg.ssl_key ),
                cert: fs.readFileSync( cfg.ssl_cert )
               }, 
               function(request, response) {
                 response.writeHeader(200, {"Content-Type": "text/html"});
                 response.write(html);
                 response.end();
               }).listen(8081, "0.0.0.0");
    
        var wss = new WebSocketServer( { server: app } );
    
        wss.on( 'connection', function ( wsConnect ) {
            wsConnect.on( 'message', function ( message ) {
                console.log( message );
            });
        });    
    })
    
    • require('fs')
    • The Node.js file system module allows you to work with the file system on your computer. To include the File System module, use the require('fs') method

    You can use the following to test the WebSocket over SSL/TLS:
    
    var ws = new WebSocket("wss://localhost:8080");
    
    But, the self-signed certificate can't pass the security enforced by the Web browser.
    You need to add the self-signed root certificate in the Web browser.
    • ERR_CERT_AUTHORITY_INVALID
    • To fix it, you need to add your CA's certificate on the client. For Chrome, Settings --> Privacy and security --> Manage certificates (chrome://settings/certificates), on the "Authorities" tab, import your self-signed root certificate.
    • ERR_CERT_COMMON_NAME_INVALID

    Origin-Based Security Model


    Origin validation is server-side verification.
    This is used to check if the request's Origin is expected by the server.


    CHAPTER 7 Debugging and Tools


    CHAPTER 8 WebSocket Protocol


    TLS


    留言

    熱門文章