8
Implementing Socket Services in Node.js

An important part of backend services is the ability to communicate with each other over sockets. Sockets allow one process to communicate with another process through an IP address and port. This can be useful when implementing interprocess communication (IPC) for two different processes running on the same server or accessing a service running on a completely different server. Node.js provides the net module that allows you to create both socket servers and clients that can connect to socket servers. For secure connections, Node.js provides the tls module that allows you to implement secure TLS socket servers and clients.

Understanding Network Sockets

Network sockets are endpoints of communication that flow across a computer network. Sockets live below the HTTP layer and provide the actual point-to-point communication between servers. Virtually all Internet communication is based on Internet sockets that flow data between two points on the Internet.

A socket works using a socket address, which is a combination of an IP address and port. There are two types of points in a socket connection: a server that listens for connections and a client that opens a connection to the server. Both the server and the client require a unique IP address and port combination.

The Node.js net module sockets communicate by sending raw data using the Transmission Control Protocol (TCP). This protocol is responsible for packaging the data and guaranteeing that it is sent from point to point successfully. Node.js sockets implement the Duplex stream, which allows you to read and write streamed data between the server and client.

Sockets are the underlying structure for the http module. If you do not need the functionality for handling web requests like GET and POST and you just need to stream data from point to point, then using sockets gives you a lighter weight solution and a bit more control.

Sockets are also handy when communicating with other processes running on the same computer. Processes cannot share memory directly, so if you want to access the data in one process from another process, you can open up the same socket in each process and read and write data between the two processes.

Understanding TPC Server and Socket Objects

To use the net module in Node.js applications, you first need to understand the TCP Server and Socket objects. These objects provide all the framework for starting a TCP server to handle requests and implementing TCP socket clients to make requests to the socket servers. Once you understand the events, properties, methods, and behavior of these objects, it will be simple to implement your own TCP socket servers and clients.

The following sections cover the purpose and behavior of the net.Socket and net.Server objects. The most important events, properties, and methods that each provides are also covered.

The net.Socket Object

Socket objects are created on both the socket server and the socket client and allow data to be written and read back and forth between them. The Socket object implements a Duplex stream, so it provides all the functionality that Writable and Readable streams provide. For example, you can use the write()method to stream writes of data to the server or client and a data event handler to stream data from the server or client.

On the socket client, the Socket object is created internally when you call net.connect() or net.createConnection(). This object is intended to represent the socket connection to the server. You use the Socket object to monitor the connection, send data to the server, and handle the response back from the server. There is no explicit client object in the Node.js net module because the Socket object acts as the full client allowing you to send/receive data and terminate the connection.

On the socket server, the Socket object is created when a client connects to the server and is passed to the connection event handler. This object is intended to represent the socket connection to the client. On the server, you use the Socket object to monitor the client connection as well as send and receive data to and from the client.

To create a Socket object, you use one of the following methods. All the calls return a Socket object. The only difference is the first parameters that they accept. The final parameter for all of them is a callback function that is executed when a connection is opened to the server. Notice that for each method there is a net.connect() and a net.createConnection() form. These work exactly the same way:

net.connect(options, [connectionListener])
net.createConnection(options, [connectionListener])
net.connect(port, [host], [connectListener])
net.createConnection(port, [host], [connectListener])
net.connect(path, [connectListener])
net.createConnection(path, [connectListener])

The first method to create a Socket object is to pass an options parameter, which is an object that contains properties that define the socket connection. Table 8.1 lists the properties that can be specified when creating the Socket object. The second method accepts port and host values, described in Table 8.1, as direct parameters. The third option accepts a path parameter that specifies a file system location that is a Unix socket to use when creating the Socket object.

Table 8.1 Options that can be specified when creating a Socket

Property

Description

port

Port number the client should connect to. This option is required.

host

Domain name or IP address of the server that the client should connect to. Defaults to localhost.

localAddress

Local IP address the client should bind to for network connections.

localPort

The local port that it binds to for network connections.

family

Version of IP stack. (default: 4)

lookup

Custom lookup. (default: dns.lookup)

Once the Socket object is created, it provides several events that are emitted during the life cycle of the connection to the server. For example, the connect event is triggered when the socket connects, the data event is emitted when there is data in the Readable stream ready to be read, and the close event is emitted when connection to the server is closed. As you implement your socket server, you can register callbacks to be executed when these events are emitted to handle opening and closing the socket, reading and writing data, and so on. Table 8.2 lists the events that can be triggered on the Socket object.

Table 8.2 Events that can be triggered on Socket objects

Event

Description

connect

Emitted when a connection is successfully established with the server. The callback function does not accept any parameters.

data

Emitted when data is received on the socket. If no data event handler is attached, then data can be lost. The callback function must accept a parameter, which is a Buffer object containing the chunk of data that was read from the socket. For example:

function(chunk){}

end

Emitted when the server terminates the connection by sending a FIN. The callback function does not accept any parameters.

timeout

Emitted when the connection to the server times out due to inactivity.

drain

Emitted when the write buffer becomes empty. You can use this event to throttle back the data stream being written to the socket. The callback function does not accept any parameters.

error

Emitted when an error occurs on the socket connection. The callback function should accept the error as the only argument. For example:

function(error){}

close

Emitted when the socket has fully closed, either because it was closed by an end() or because an error occurred. The callback function does not accept any parameters.

The Socket object also includes several methods that allow you to do things like read from and write to the socket as well as pause or end data flow. Many of these are inherited from the Duplex stream objects and should be familiar to you. Table 8.3 lists the methods available on the Socket object.

Table 8.3 Methods that can be called on Socket Objects

Method

Description

setEncoding([encoding])

When this function is called, data returned from the socket’s streams is an encoded String instead of a Buffer object. Sets the default encoding that should be used when writing data to and reading data from the streams. Using this option handles multi-byte characters that might otherwise be mangled when converting the Buffer to a string using buf.toString(encoding). If you want to read the data as strings, always use this method.

write(data, [encoding], [callback])

Writes a data Buffer or String to the Writable stream of the socket using the encoding if specified. The callback function is executed as soon as the data is written.

end([data], [encoding])

Writes a data Buffer or String to the Writable stream of the socket and then flushes the stream and closes the connection.

destroy()

This forces the socket connection to shut down. You should only need to use this in the case of failures.

pause()

Pauses a Readable stream of the socket from emitting data events. This allows you to throttle back the upload of data to the stream.

resume()

Resumes data event emitting on the Readable stream of the socket.

setTimeout(timeout, [callback])

Specifies a timeout in milliseconds that the server will wait before emitting a timeout event when the socket is inactive. The callback function will be triggered as a once event listener. If you want the connection to be terminated on timeout, you should do it manually in the callback function.

setNoDelay([noDelay])

Disables/enables the Nagle algorithm that buffers data before sending it. Setting this to false disables data buffering.

setKeepAlive([enable], [initialDelay])

Enables/disables the keep-alive functionality on the connection. The optional initialDelay parameter specifies the amount in milliseconds that the socket is idle before sending the first keep-alive packet.

address()

Returns the bound address, the address family name, and the port of the socket as reported by the operating system. The return value is an object that contains the port, family, and address properties. For example:

{ port: 8107, family: 'IPv4', address: '127.0.0.1' }

unref()

Calling this method allows the Node.js application to terminate if this socket is the only event on the event queue.

ref()

References this socket so that if this socket is the only thing on the event queue, the Node.js application will not terminate.

The Socket object also provides several properties that you can access to get information about the object. For example, the address and port the socket is communicating on, the amount of data being written, and the buffer size. Table 8.4 lists the properties available on the Socket object.

Table 8.4 Properties that can be accessed on Socket Objects

Method

Description

bufferSize

Returns the number of bytes currently buffered waiting to be written to the socket’s stream

remoteAddress

IP address of the remote server that the socket is connected to

remotePort

Port of the remote server that the socket is connected to

remoteFamily

IP of the remote family the socket is connected to

localAddress

Local IP address the remote client is using for the socket connection

localPort

Local port the remote client is using for the socket connection

bytesRead

Number of bytes read by the socket

bytesWritten

Number of bytes written by the socket

To illustrate flowing data across a Socket object, the following code shows the basics of implementing the Socket object on a client. Notice that the net.connect() method is called using an options object containing a port and a host attribute. The connect callback function logs a message and then writes some data out to the server. To handle data coming back from the server, the on.data() event handler is implemented. To handle the closure of the socket, the on('end') event handler is implemented:

var net = require('net');
var client = net.connect({port: 8107, host:'localhost'}, function() {
  console.log('Client connected');
  client.write('Some Data
');
});
client.on('data', function(data) {
  console.log(data.toString());
  client.end();
});
client.on('end', function() {
  console.log('Client disconnected');
});

The net.Server Object

The net.Server object is used to create a TCP socket server and begin listening for connections to which you will be able to read and write data. The Server object is created internally when you call net.createServer(). This object represents the socket server and handles listening for connections and then sending and receiving data on those connections to the server.

When the server receives a connection, the Server creates a Socket object and passes it to any connection event handlers that are listening. Because the Socket object implements a Duplex stream, you can use the write()method to stream writes of data back to the client and a data event handler to stream data from the client.

To create a Server object, you use the net.createServer() method shown here:

net.createServer([options], [connectionListener])

The options parameter is an object that specifies options to use when creating the socket Server object. Table 8.5 lists the properties of the options object. The second parameter is the connection event callback function, which is executed when a connection is received. This connectionListener callback function is passed to the Socket object for the connecting client.

Table 8.5 Options that can be specified when creating a net.Server

Property

Description

allowHalfOpen

A Boolean; when true, the socket won’t automatically send a FIN packet when the other end of the socket sends a FIN packet, thus allowing half of the Duplex stream to remain open. Defaults to false.

pauseOnConnect

A Boolean; when true, each socket for each connection is paused, and no data will be read from its handle. This allows processes to pass connections between them without reading any data. Defaults to false.

Once the Server object is created, it provides several events that are triggered during the life cycle of the server. For example, the connection event is triggered when a socket client connects, and the close event is triggered when the server shuts down. As you implement your socket server, you can register callbacks to be executed when these events are triggered to handle connections, errors, and shutdown. Table 8.6 lists the events that can be triggered on the Socket object.

Table 8.6 Events that can be triggered on Socket objects

Event

Description

listening

Emitted when the server begins listening on a port by calling the listen() method. The callback function does not accept any parameters.

connection

Emitted when a connection is received from a socket client. The callback function must accept a parameter that is a Socket object representing the connection to the connecting client. For example:

function(client){}

close

Emitted when the server closes either normally or on error. This event is emitted until all client connections have ended.

error

Emitted when an error occurs. The close event also is triggered on errors.

The Server object also includes several methods that allow you to do things like read from and write to the socket as well as pause or end data flow. Many of these are inherited from the Duplex stream objects and should be familiar to you. Table 8.7 lists the methods available on the Socket object.

Table 8.7 Methods that can be called on Socket objects

Method

Description

listen(port, [host], [backlog], [callback])

Opens up a port on the server and begins listening for connections. port specifies the listening port. If you specify 0 for the port, a random port number is selected. host is the IP address to listen on. If it is omitted, the server accepts connections directed to any IPv4 address. backlog specifies the maximum number of pending connections the server will allow. The default is 511.

The callback function is called when the server has opened the port and begins listening.

listen(path, [callback])

Same as the preceding method except that a Unix socket server is started to listen for connections on the file system path specified.

listen(handle, [callback])

Same as the preceding method except that a handle to a Server or Socket object has an underlying _handle member that points to a file descriptor handle on the server. It assumes that the file descriptor points to a socket file that has been bound to a port already.

getConnections(callback)

Returns the number of connections currently connected to the server. The callback is executed when the number of connections is calculated and accepts an error parameter and a count parameter. For example:

function(error, count)

close([callback])

Stops the server from accepting new connections. Current connections are allowed to remain until they complete. The server does not truly stop until all current connections have been closed.

address()

Returns the bound address, the address family name, and the port of the socket as reported by the operating system. The return value is an object that contains the port, family, and address properties. For example:

{ port: 8107, family: 'IPv4', address: '127.0.0.1' }

unref()

Calling this method allows the Node.js application to terminate if this server is the only event on the event queue.

ref()

References this socket so that if this server is the only thing on the event queue the Node.js application will not terminate.

The Server object also provides the maxConnections attribute, which allows you to set the maximum number of connections that the server will accept before rejecting them. If a process has been forked to a child for processing using child_process.fork(), you should not use this option.

The following code shows the basics of implementing the Server object. Notice that the net.createServer() method is called and implements a callback that accepts the client Socket object. To handle data coming back from the client, the on.data() event handler is implemented. To handle the closure of the socket, the on('end') event handler is implemented. To begin listening for connections, the listen() method is called on port 8107:

var net = require('net');
var server = net.createServer(function(client) {
  console.log(Client connected');
  client.on('data', function(data) {
    console.log('Client sent ' + data.toString());
  });
  client.on('end', function() {
    console.log('Client disconnected');
  });
  client.write('Hello');
});
server.listen(8107, function() {
  console.log('Server listening for connections');
});

Implementing TCP Socket Servers and Clients

Now that you understand the net.Server and net.Socket objects, you are ready to implement some Node.js TCP clients and servers. This section guides you through the process of implementing basic TCP clients and servers in Node.js.

The examples in the following sections are basic to make it easy for you to grasp the concepts of starting the TCP server listening on a port, and then implementing clients that can connect. The examples are designed to help you see the interactions and event handling that need to be implemented.

Implementing a TCP Socket Client

At the most basic level, implementing a TCP socket client involves the process of creating a Socket object that connects to the server, writing data to the server, and then handling the data that comes back. Additionally, you should build the socket so that it can also handle errors, the buffer being full, and timeouts. This section discusses each of the steps to implement a socket client using the Socket object. Listing 8.1 presents the full code for the following discussion.

The first step is to create the socket client by calling net.connect() as shown below. Pass in the port and host that you want to connect to as well and implement a callback function to handle the connect event:

net.connect({port: 8107, host:'localhost'}, function() {
  //handle connection
});

Then inside the callback you should set up the connection behavior. For example, you may want to add a timeout or set the encoding as shown here:

this.setTimeout(500);
this.setEncoding('utf8');

You also need to add handlers for the data, end, error, timeout, and close events that you want to handle. For example, to handle the data event so that you can read data coming back from the server, you might add the following handler once the connection has been established:

this.on('data', function(data) {
  console.log("Read from server: " + data.toString());
  //process the data
  this.end();
});

To write data to the server, you implement a write() command. If you are writing a lot of data to the server and the write fails, then you may also want to implement a drain event handler that begins writing again when the buffer is empty. The following shows an example of implementing a drain handler because of a write failure. Notice that a closure is used to preserve the values of the socket and data variables once the function has ended.

function writeData(socket, data){
  var success = !socket.write(data);
  if (!success){
    (function(socket, data){
      socket.once('drain', function(){
        writeData(socket, data);
      });
    })(socket, data);
  }
}

Listing 8.1 shows the full implementation of a basic TCP socket client. All the client does is send a bit of data to the server and receive a bit of data back; however, the example could easily be expanded to support more complex data handling across the socket. Notice that three separate sockets are opened to the server and are communicating at the same time. Notice that each client created gets a different random port number, as shown in Listing 8.1 Output.

Listing 8.1 socket_client.js: Implementing basic TCP socket clients

01 var net = require('net');
02 function getConnection(connName){
03   var client = net.connect({port: 8107, host:'localhost'}, function() {
04     console.log(connName + ' Connected: ');
05     console.log('   local = %s:%s', this.localAddress, this.localPort);
06     console.log('   remote = %s:%s', this.remoteAddress, this.remotePort);
07     this.setTimeout(500);
08     this.setEncoding('utf8');
09     this.on('data', function(data) {
10       console.log(connName + " From Server: " + data.toString());
11       this.end();
12     });
13     this.on('end', function() {
14       console.log(connName + ' Client disconnected');
15     });
16     this.on('error', function(err) {
17       console.log('Socket Error: ', JSON.stringify(err));
18     });
19     this.on('timeout', function() {
20       console.log('Socket Timed Out');
21     });
22     this.on('close', function() {
23       console.log('Socket Closed');
24     });
25   });
26   return client;
27 }
28 function writeData(socket, data){
29   var success = !socket.write(data);
30   if (!success){
31     (function(socket, data){
32       socket.once('drain', function(){
33         writeData(socket, data);
34       });
35     })(socket, data);
36   }
37 }
38 var Dwarves = getConnection("Dwarves");
39 var Elves = getConnection("Elves");
40 var Hobbits = getConnection("Hobbits");
41 writeData(Dwarves, "More Axes");
42 writeData(Elves, "More Arrows");
43 writeData(Hobbits, "More Pipe Weed");<Listing First>

Listing 8.1 Output socket_client.js: Implementing basic TCP socket clients

Elves Connected:
   local = 127.0.0.1:62616
   remote = 127.0.0.1:8107
Dwarves Connected:
   local = 127.0.0.1:62617
   remote = 127.0.0.1:8107
Hobbits Connected:
   local = 127.0.0.1:62618
   remote = 127.0.0.1:8107
Elves From Server: Sending: More Arrows
Dwarves From Server: Sending: More Axes
Hobbits From Server: Sending: More Pipe Weed
Dwarves Client disconnected
Socket Closed
Elves Client disconnected
Socket Closed
Hobbits Client disconnected
Socket Closed

Implementing a TCP Socket Server

At the most basic level, implementing a TCP server client involves the process of creating a Server object, listening on a port, and then handling incoming connections, including reading and writing data to and from the connections. Additionally, the socket server should handle the close and error events on the Server object as well as the events that occur in the incoming client connection Socket object. This section discusses each of the steps to implement a socket server using the Server object. Listing 8.2 presents the full code for the following discussion.

The first step is to create the socket server by calling net.createServer() as shown below. You also need to provide a connection callback handler and then call listen() to begin listening on the port:

var server = net.createServer(function(client) {
  //implement the connection callback handler code here.
});
server.listen(8107, function() {
 //implement the listen callback handler here.
});

Inside the listen callback handler, you should also add handlers to support the close and error events on the Server object. These may just be log statements, or you may also want to add additional code that is executed when these events occur. The follow shows the basic examples:

server.on('close', function(){
  console.log('Server Terminated');
});
server.on('error', function(err){
});

Inside the connection event callback, you need to set up the connection behavior. For example, you might want to add a timeout or set the encoding as shown here:

this.setTimeout(500);
this.setEncoding('utf8');

You also need to add handlers for the data, end, error, timeout, and close events that you want to handle on the client connection. For example, to handle the data event so that you can read data coming from the client, you might add the following handler once the connection is established:

this.on('data', function(data) {
  console.log("Received from client: " + data.toString());
  //process the data
});

To write data to the server, you implement a write() command somewhere in your code. If you are writing a lot of data to the client, then you may also want to implement a drain event handler that begins writing again when the buffer is empty. This can help if the write() returns a failure because the buffer is full, or if you want to throttle back writing to the socket. The following shows an example of implementing a drain handler because of a write failure. Notice that a closure is used to preserve the values of the socket and data variables once the function has ended:

function writeData(socket, data){
  var success = !socket.write(data);
  if (!success){
    (function(socket, data){
      socket.once('drain', function(){
        writeData(socket, data);
      });
    })(socket, data);
  }
}

The code in Listing 8.2 shows the full implementation of a basic TCP socket server. The socket server accepts connections on port 8107, reads the data in, and then writes a string back to the client. Although the implementation is basic, it illustrates handling the events as well as reading and writing data in the client connection.

Listing 8.2 socket_server.js: Implementing a basic TCP socket server

01 var net = require('net');
02 var server = net.createServer(function(client) {
03   console.log('Client connection: ');
04   console.log('   local = %s:%s', client.localAddress, client.localPort);
05   console.log('   remote = %s:%s', client.remoteAddress, client.remotePort);
06   client.setTimeout(500);
07   client.setEncoding('utf8');
08   client.on('data', function(data) {
09     console.log('Received data from client on port %d: %s',
10                 client.remotePort, data.toString());
11     console.log('  Bytes received: ' + client.bytesRead);
12     writeData(client, 'Sending: ' + data.toString());
13     console.log('  Bytes sent: ' + client.bytesWritten);
14   });
15   client.on('end', function() {
16     console.log('Client disconnected');
17     server.getConnections(function(err, count){
18       console.log('Remaining Connections: ' + count);
19     });
20   });
21   client.on('error', function(err) {
22     console.log('Socket Error: ', JSON.stringify(err));
23   });
24   client.on('timeout', function() {
25     console.log('Socket Timed Out');
26   });
27 });
28 server.listen(8107, function() {
29   console.log('Server listening: ' + JSON.stringify(server.address()));
30   server.on('close', function(){
31     console.log('Server Terminated');
32   });
33   server.on('error', function(err){
34     console.log('Server Error: ', JSON.stringify(err));
35   });
36 });
37 function writeData(socket, data){
38   var success = !socket.write(data);
39   if (!success){
40     (function(socket, data){
41       socket.once('drain', function(){
42         writeData(socket, data);
43       });
44     })(socket, data);
45   }
46 }

Implementing TLS Servers and Clients

Transport Layer Security/Secure Socket Layer (TLS/SSL) is a cryptographic protocol designed to provide secure communications on the Internet. They use X.509 certificates along with session keys to verify whether the socket server you are communicating with is the one that you want to communicate with. TLS provides security in two main ways. First, it uses long-term public and secret keys to exchange a short-term session key so that data can be encrypted between client and server. Second, it provides authentication so that you can ensure that the webserver you are connecting to is the one you actually think it is, thus preventing man-in-the-middle attacks where requests are rerouted through a third party.

The following sections discuss implementing TLS socket servers and clients in your Node.js environment using the tls module. Before getting started using TLS, you need to generate a private key and public certificate for both your clients and your server. There are several ways to do this depending on your platform. One of the simplest methods is to use the OpenSSL library for your platform.

To generate the private key, first execute the following OpenSSL command:

openssl genrsa -out server.pem 2048

Next, use the following command to create a certificate signing request file:

openssl req -new -key server.pem -out server.csr

Note

When creating the certificate signing request file, you are asked several questions. When prompted for the Common Name, you should put in the domain name of the server you want to connect to. Otherwise, the certificate will not work. Also you can put in additional domain names and IP addresses in the Subject Alternative Names field.

Then to create a self-signed certificate that you can use for your own purpose or testing, use the following command:

openssl x509 -req -days 365 -in server.csr -signkey server.pem -out server.crt

Note

The self-signed certificate is fine for testing purposes or internal use. However, if you are implementing an external web service that needs to be protected on the Internet, you may want to get a certificate signed by a certificate authority. If you want to create a certificate that is signed by a third-party certificate authority, you need to take additional steps.

Creating a TLS Socket Client

Creating a TLS client is almost exactly like the process of creating a socket client discussed earlier in this chapter. The only difference is that there are additional options, shown in Table 8.8, that allow you to specify the security options for the client. The most important options you need to worry about are key, cert, and ca.

The key option specifies the private key used for SSL. The cert value specifies the x509 public key to use. If you are using a self-signed certificate, you need to point the ca property at the certificate for the server:

var options = {
  key: fs.readFileSync('test/keys/client.pem'),
  cert: fs.readFileSync('test/keys/client.crt'),
  ca: fs.readFileSync('test/keys/server.crt')
};

Once you have defined the options with the cert, key, and ca settings, then you can call the tls.connect(options, [responseCallback]), and it will work exactly the same as the net.connect() call. The only difference is that the data between the client and server is encrypted.

var options = {
  hostname: 'encrypted.mysite.com',
  port: 8108,
  path: '/',
  method: 'GET',
  key: fs.readFileSync('test/keys/client.pem'),
  cert: fs.readFileSync('test/keys/client.crt'),
  ca: fs.readFileSync('test/keys/server.crt')
};
var req = tls.connect(options, function(res) {
  <handle the connection the same as an net.connect>
})

Table 8.8 Additional options for tls.connect()

Event

Description

pfx

A string or Buffer object containing the private key, certificate, and CA certs of the server in PFX or PKCS12 format.

key

A string or Buffer object containing the private key to use for SSL.

passphrase

A string containing the passphrase for the private key or pfx.

cert

A string or Buffer object containing the public x509 certificate to use.

ca

An array of strings or buffers of trusted certificates in PEM format to check the remote host against.

rejectUnauthorized

A Boolean; when true, the server certificate is verified against the list of supplied CAs. An error event is emitted if verification fails. Verification happens at the connection level, before the HTTP request is sent. Defaults to true.

servername

Specifies the server name for the Server Name Indication SNI TLS extension.

secureProtocol

Specifies the SSL method to use. For example, SSLv3_method will force SSL version 3.

Creating a TLS Socket Server

Creating a TLS socket server is almost exactly like the process of creating a socket server discussed earlier in this chapter. The only differences are that there are additional options parameters that you must pass into https.createServer(), and there are some additional events that can be triggered on the tls.Server object. The options, listed in Table 8.9, allow you to specify the security options for the server. Table 8.10 lists the additional events for the TLS socket server. The most important options you need to worry about are key, cert, and ca.

The key option specifies the private key used for SSL. The cert value specifies the x509 public key to use. If you are using a self-signed certificate, you need to point the ca property at the certificate for the client.

Table 8.9 Additional options for tls.createServer()

Event

Description

pfx

A string or Buffer object containing the private key, certificate, and CA certs of the server in PFX or PKCS12 format.

key

A string or Buffer object containing the private key to use for SSL.

passphrase

A string containing the passphrase for the private key or pfx.

cert

A string or Buffer object containing the public x509 certificate to use.

ca

An array of strings or buffers of trusted certificates in PEM format to check the remote host against.

crl

Either a string or list of strings of PEM encoded CRLs (Certificate Revocation Lists).

ciphers

A string describing the ciphers to use or exclude. Using this in conjunction with the honorCipherOrder is a good way to prevent BEAST attacks.

handshakeTimeout

Specifies the number of milliseconds to wait before aborting the connection if the SSL/TLS handshake does not finish. If the timeout is hit, a clientError is emitted on the tls.Server.

honorCipherOrder

A Boolean; when true, the server honors the server’s preferences over the client’s when choosing a cipher.

requestCert

When true, the server requests a certificate from clients that connect and attempt to verify that certificate. Default is false.

rejectUnauthorized

When true, the server rejects any connection that is not authorized by the list of supplied CAs. This option only has an effect if requestCert is true. Default is false.

NPNProtocols

An Array or Buffer of possible NPN protocols. Protocols should be ordered by their priority.

SNICallback

A function that is called if the client supports the SNI TLS extension. The server name is the only argument passed to the callback.

sessionIdContext

A string containing an opaque identifier for session resumption. If requestCert is true, the default is an MD5 hash value generated from the command line. Otherwise, the default is not provided.

secureProtocol

Specifies the SSL method to use. For example, SSLv3_method will force SSL version 3.

The following shows an example of creating a TLS socket server in Node.js:

var options = {
  key: fs.readFileSync('test/keys/server.pem'),
  cert: fs.readFileSync('test/keys/server.crt'),
  ca: fs.readFileSync('test/keys/client.crt')
};
tls.createServer(options, function (client) {
  client.write("Hello Secure World
");
  client.end();
}).listen(8108);

Once the TLS socket server has been created, the request/response handling works basically the same way that the TCP socket servers described earlier in this chapter work. The server can accept connections and read and write data back to the client.

Table 8.10 Additional events on TLS Server objects

Event

Description

secureConnection

Emitted when a new secure connection has been successfully established. The callback accepts a single instance of a tls.CleartextStream streaming object that can be written to and read from. For example:

function (clearStream)

clientError

Emitted when a client connection emits an error. The parameters to the callback are the error and a tls.SecurePair object. For example:

function (error, securePair)

newSession

Emitted when a new TLS session is created. The callback is passed the sessionId and sessionData parameters containing the session information. For example:

function (sessionId, sessionData)

resumeSession

Emitted when the client tries to resume a previous TLS session. You can store the session in an external storage so that you can look it up when receiving this event. The callback handler receives two parameters. The first is a sessionId, and the second is a callback to be executed if the session cannot be established. For example:

function (sessionId, callback)

Summary

Sockets are useful when implementing backend services in a Node.js application. They allow a service on one system to communicate with a service on another system through an IP address and port. They also provide the ability to implement an IPC between two different processes running on the same server. The net module allows you to create Server objects that act as socket servers and Socket objects that act as socket clients. Since the Socket object extends Duplex streams, you can read and write data from both the server and the client. For secure connections, Node.js provides the tls module that allows you to implement secure TLS socket servers and clients.

Next

In the next chapter, you learn how to implement multiprocessing in a Node.js environment. This allows you to farm work out to other processes on the system to take advantage of multiprocessor servers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.170.134