This article walks through the process of installing, provisioning, and running a subset of the samples that are included with the OpenDXL JavaScript Client.
The samples examined in this article demonstrate how to use the two distinct messaging models that are supported by DXL. A publish/subscribe event-based model and a service-based model with point-to-point (request/response) communication.
Client Installation and Provisioning
The following steps detail how to install and provision the OpenDXL JavaScript Client:
- Download the latest version of the OpenDXL JavaScript Client
- Extract the release .zip file
- Follow the client installation instructions to install the client
- The client libraries are in the lib folder of the extracted release
- Follow the steps to provision the client for use with the samples
- The dxlclient.config file located in the sample sub-directory of the JavaScript DXL SDK must be populated.
- The steps will vary based on the type of DXL fabric being connected to
Event-based Communication
The DXL fabric supports an event-based communication model. This model is typically referred to as “publish/subscribe” wherein clients register interest by subscribing to a particular topic and publishers periodically send events to that topic. The event is delivered by the DXL fabric to all of the currently subscribed clients for the topic. Therefore, a single event sent can reach multiple clients (one-to-many). It is important to note that in this model the client passively receives events when they are sent by a publisher.
For example, McAfee Advanced Threat Defense (ATD) servers send events to the topic /mcafee/event/atd/file/report when they have successfully determined the reputation of a submitted file. Any clients currently subscribed to this topic will receive the report and can take immediate action.
Run Sample
A "basic event sample" is included with the OpenDXL JavaScript Client SDK that demonstrates sending and receiving 1000 events via the JavaScript client.
To run the sample execute the following from the root directory of the extracted OpenDXL JavaScript Client SDK:
$ node sample/basic/event-example.js
Sample Output
The output should appear similar to the following (1000 events are received and timing information is displayed).
Code Details
This section walks through the different portions of code that comprise the "basic event sample" in detail. Full code for the sample is available within the extracted SDK or via the OpenDXL JavaScript Client GitHub Repository.
As shown above, several variables are defined in the header of the "basic event sample":
-
EVENT_TOPIC: The topic that the events will be sent (and received) on:
- /isecg/sample/basicevent
- TOTAL_EVENTS: The total number of events to send (1000)
- eventCount: Tracks the number of events that have been received
- start: The start time of the sample
The next portion of code (shown above) creates the DXL client. The first step is to create a DXL Config object that contains the information necessary to connect to a DXL fabric. In this particular sample, the createDxlConfigFromFile method was used to create a configuration object from a configuration file.
The next line of code creates a DXL Client object by passing the previously created configuration object to its constructor.
- // Create and add event listener
- client.addEventCallback(EVENT_TOPIC,
- function (event) {
- // Print the payload for the received event. The toString() call converts
- // the payload from a binary Buffer into a string, decoded using UTF-8
- // character encoding.
- console.log('Received event: ' + event.payload.toString())
- // Increment the count
- eventCount++
- // Wait until all events have been received
- if (eventCount === TOTAL_EVENTS) {
- console.log('Elapsed time (ms): ' + (Date.now() - start))
- // Destroy the client - frees up resources so that the application
- // stops running
- client.destroy()
- }
- })
The next portion of code (shown above) registers an event callback that will receive events that are published to the /isecg/sample/basiceventtopic.
The event callback function is registered with the client via the addEventCallback method. This method will be invoked for each event that is sent to the DXL fabric for topics that are associated with the callback. The main portion of this method displays the payload of the event received and increments the total count of events. If all of the events have been received, the client is stopped (and destroyed) by invoking the client's destroy method.
- // Connect to the fabric, supplying a callback function which is invoked
- // when the connection has been established
- client.connect(function () {
- // Loop and send the events
- for (var eventId = 0; eventId < TOTAL_EVENTS; eventId++) {
- // Create the event
- var event = new dxl.Event(EVENT_TOPIC)
- // Set the payload
- event.payload = eventId.toString()
- // Send the event
- client.sendEvent(event)
- }
- console.log('Waiting for events to be received...')
- })
The final portion of this sample's code (shown above) connects to the DXL fabric and sends out 1000 events on the /isecg/sample/basicevent topic.
To connect to the fabric, the connect method is invoked on the DXL client. The connect method takes an optional argument which is a callback method that will be invoked when the client has completed connecting to the DXL fabric. The body of the callback function passed in this sample contains a for loop that is iterated for TOTAL_EVENTS times (1000). The first step in the body of the for loop is to construct a new Event that will be sent to the /isecg/sample/basicevent topic. Next, a payload is set on the event that contains the index of the loop (from 0 to 999). The final line of the loop body sends the event to the DXL fabric by invoking the sendEvent method on the client.
Service-based Communication
The DXL fabric allows for “services” to be registered and exposed that respond to requests sent by invoking clients. This communication is point-to-point (one-to-one), meaning the communication is solely between an invoking client and the service that is being invoked. It is important to note that in this model the client actively invokes the service by sending it requests.
For example, the McAfee Threat Intelligence Exchange (TIE) service is exposed via DXL allowing for DXL clients to actively request reputations for files and certificates.
Run Sample
A "basic service sample" is included with the OpenDXL JavaScript Client SDK that demonstrates registering and invoking a DXL service.
To run the sample execute the following from the root directory of the extracted OpenDXL JavaScript Client SDK:
$ node sample/basic/service-example.js
Sample Output
The output should appear similar to the following (a request is received by the service and it delivers a response back to the invoking client).
Code Details
This section walks through the different portions of code that comprise the "basic service sample" in detail. Full code for the sample is available within the extracted SDK or via the OpenDXL JavaScript Client GitHub Repository.
As shown above, a variable is defined that contains a topic to associate with the service. Multiple topics can be associated with a service, each topic represents a different "function" that can be invoked by remote DXL clients.
- // Create service registration object
- var info = new dxl.ServiceRegistrationInfo(client, 'myService')
- // Add a topic for the service to respond to
- info.addTopic(SERVICE_TOPIC,
- // Handle the receipt of an incoming service request
- function (request) {
- // Extract information from request. The toString() call converts the
- // payload from a binary Buffer into a string, decoded using UTF-8
- // character encoding.
- console.log('Service received request payload: ' +
- request.payload.toString())
- // Create the response message
- var response = new dxl.Response(request)
- // Populate the response payload
- response.payload = 'pong'
- // Send the response
- client.sendResponse(response)
- })
The next portion of code (shown above) creates a DXL service registration that will respond to requests that are received on the /isecg/sample/mybasicservice topic.
The first step is to create a ServiceRegistrationInfo object that will be used to register the service. Note that the ServiceRegistrationInfo object is created with a name ("myService") to represent the specific instance of the service during registration.
The next step is to register a callback (seen above as the second argument passed into info.addTopic()). This callback function will be invoked for each request that is sent to the DXL fabric for the service topic that is associated with the callback.
The implementation of the callback function displays the payload of the Request message that was received. Next, it creates a Response message that will be sent back to the invoking client. The payload of the Response message is set to "pong". The last line of this function sends the response back to the invoking client by passing the newly created Response object to the sendResponse method of the client.
- // Register the service with the fabric
- client.registerServiceAsync(info,
- function (error) {
- if (error) {
- // Destroy the client - frees up resources so that the application
- // stops running
- client.destroy()
- console.log('Error registering service: ' + error.message)
- // If an error did not occur, invoke the service (send a request)
- } else {
- // Create the request message
- var request = new dxl.Request(SERVICE_TOPIC)
- // Populate the request payload
- request.payload = 'ping'
- // Send the request
- client.asyncRequest(request,
- // Handle the response to the request
- function (error, response) {
- // Destroy the client - frees up resources so that the application
- // stops running
- client.destroy()
- // Display the contents of an error, if one occurred
- if (error) {
- console.log('Request error: ' + error.message)
- // The 'code' property, if set, typically has a string
- // representation of the error code.
- if (error.code) {
- console.log('Request error code: ' + error.code)
- // If no string representation is available for the error code
- // but the error is a DXL 'RequestError', a numeric error
- // code should be available in the
- // 'dxlErrorResponse.errorCode' property.
- } else if (error.dxlErrorResponse) {
- console.log('Request error code: ' +
- error.dxlErrorResponse.errorCode)
- }
- // No error occurred, so extract information from the response. The
- // toString() call converts the payload from a binary Buffer into a
- // string, decoded using UTF-8 character encoding.
- } else {
- console.log('Client received response payload: ' +
- response.payload.toString())
- }
- })
- }
- })
The next portion of code (shown above) registers and invokes the service. If the invocation of the service is successful, information contained in the received response is displayed.
The first step is to register the service by invoking the registerServiceAsync method of the client, passing in the service registration object that was previously created along with a callback function that will be invoked once registration has completed.
Once registration of the service has completed, the specified callback function will be invoked. The main portion of the callback creates a Request message that will be sent to the /isecg/sample/basicservice topic. The payload of the Request object is set to "ping".
Next, the service is invoked by passing the newly created Request object to the asyncRequest method of the client. The second argument to the asyncRequest method is a callback function that will be invoked when a Response has been received for the Request (or an error has occurred).
If an error has not occurred, the payload associated with the Response message is displayed.