Skip to content

Commit

Permalink
ppapi: PPB_VpnProvider: Add a simple NaCl SDK example.
Browse files Browse the repository at this point in the history
Co-Authored-By: Valentin Ilie <valentin.ilie@intel.com>

BUG=506490

Review-Url: https://codereview.chromium.org/1731273002
Cr-Commit-Position: refs/heads/master@{#406516}
  • Loading branch information
adrianbelgun authored and Commit bot committed Jul 20, 2016
1 parent 98fea99 commit bb921df
Show file tree
Hide file tree
Showing 8 changed files with 531 additions and 0 deletions.
1 change: 1 addition & 0 deletions native_client_sdk/src/build_tools/sdk_files.list
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ examples/api/var_array_buffer/*
examples/api/var_dictionary/*
examples/api/video_decode/*
examples/api/video_encode/*
examples/api/vpn_provider/*
examples/api/websocket/*
examples/button_close.png
examples/button_close_hover.png
Expand Down
21 changes: 21 additions & 0 deletions native_client_sdk/src/examples/api/vpn_provider/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The VpnProvider example demonstrates how to use the VpnProvider API.

This NaCl SDK example can only be used as an extension on Chrome OS.

== Compile ==
Run "make" from this folder, then open up chrome://extensions
in the browser select "Pack extension...", browse to examples/api/vpn_provider,
click on "Open", then on "Pack extension" and "OK". Copy the .crx to the
Chromebook.

== Install ==
To "sideload" an app or extension under Chrome OS, open up chrome://extensions
in the browser on the Chromebook, then open the file manager with Alt-Shift-M,
then drag the .crx file onto the extensions page.

== Test ==
After loading the extension to a Chromebook, open the 'VPN Provider' app.
Wait for the connection to be created. From the network configuration menu
select the 'Mock configuration' entry.

Observe the connection setup flow in the "VPN Provider" app.
25 changes: 25 additions & 0 deletions native_client_sdk/src/examples/api/vpn_provider/example.dsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
'TARGETS': [
{
'NAME' : 'vpn_provider',
'TYPE' : 'main',
'SOURCES' : [
'vpn_provider.cc',
'vpn_provider_helper.cc',
'vpn_provider_helper.h'
],
'LIBS': ['ppapi_cpp', 'ppapi']
}
],
'DATA': [
'example.js',
'README',
],
'DEST': 'examples/api',
'NAME': 'vpn_provider',
'TITLE': 'VPN Provider',
'GROUP': 'API',
'PERMISSIONS': [
'vpnProvider'
]
}
134 changes: 134 additions & 0 deletions native_client_sdk/src/examples/api/vpn_provider/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// VPN Configuration identification.
var configName = 'Mock configuration';
var configId;

// Example configuration.
var vpnParams = {
'address': '127.0.0.1/32',
'mtu': '1000',
'exclusionList': ['127.0.0.1/32'],
'inclusionList': ['0.0.0.0/0'],
'dnsServers': ['8.8.8.8'],
'reconnect': 'true'
};

// Simple log to HTML
function wlog(message) {
var logEl = document.getElementById('log');
logEl.innerHTML += message + '<br />'; // Append to log.
logEl.scrollTop = logEl.scrollHeight; // Scroll log to bottom.
}

// Create a VPN configuration using the createConfig method.
// A VPN configuration is a persistent entry shown to the user in a native
// Chrome OS UI. The user can select a VPN configuration from a list and
// connect to it or disconnect from it.
function create() {
chrome.vpnProvider.createConfig(configName, function(id) {
configId = id;
wlog('JS: Created configuration with name=\'' + configName + '\'' +
' and id=\'' + configId + '\'');
});
}

// Bind connection to NaCl.
function bind() {
common.naclModule.postMessage({cmd: 'bind', name: configName, id: configId});
}

function onSetParameters() {
chrome.vpnProvider.setParameters(vpnParams, function() {
wlog('JS: setParameters set!');

// Bind connection to NaCl.
bind();
});
}

function onBindSuccess() {
// Notify the connection state as 'connected'.
chrome.vpnProvider.notifyConnectionStateChanged('connected', function() {
wlog('JS: notifyConnectionStateChanged connected!');
});
}

// VpnProviders handlers.
function onPlatformMessageListener(id, message, error) {
wlog('JS: onPlatformMessage: id=\'' + id + '\' message=\'' + message +
'\' error=\'' + error + '\'');

if (message == 'connected') {
wlog('JS: onPlatformMessage connected!');

// Notify NaCl module to connect to the VPN tunnel.
common.naclModule.postMessage({cmd: 'connected'});

} else if (message == 'disconnected') {
wlog('JS: onPlatformMessage disconnected!');

// Notify NaCl module to disconnect from the VPN tunnel.
common.naclModule.postMessage({cmd: 'disconnected'});
}
}

// This function is called by common.js when a message is received from the
// NaCl module.
function handleMessage(message) {
if (typeof message.data === 'string') {
wlog(message.data);
} else if (message.data['cmd'] == 'setParameters') {
onSetParameters();
} else if (message.data['cmd'] == 'bindSuccess') {
onBindSuccess();
}
}

// setupHandlers VpnProviders handlers.
function setupHandlers() {
// Add listeners to the events onPlatformMessage, onPacketReceived and
// onConfigRemoved.
chrome.vpnProvider.onPlatformMessage.addListener(onPlatformMessageListener);

chrome.vpnProvider.onPacketReceived.addListener(function(data) {
wlog('JS: onPacketReceived');
console.log('Unexpected event:vpnProvider.onPacketReceived ' +
'called from JavaScript.');
});

chrome.vpnProvider.onConfigRemoved.addListener(function(id) {
wlog('JS: onConfigRemoved: id=\'' + id + '\'');
});

chrome.vpnProvider.onConfigCreated.addListener(function(id, name, data) {
wlog('JS: onConfigCreated: id=\'' + id + '\' name=\'' + name + '\'' +
'data=' + JSON.stringify(data));
});

chrome.vpnProvider.onUIEvent.addListener(function(event, id) {
wlog('JS: onUIEvent: event=\'' + event + '\' id=\'' + id + '\'');
});
}

// This function is called by common.js when the NaCl module is
// loaded.
function moduleDidLoad() {
// Once we load, hide the plugin. In this example, we don't display anything
// in the plugin, so it is fine to hide it.
common.hideModule();

if (chrome.vpnProvider === undefined) {
wlog('JS: moduleDidLoad: chrome.vpnProvider undefined.');
console.log('JS: moduleDidLoad: chrome.vpnProvider undefined.');
return;
}

// Setup VpnProvider handlers.
setupHandlers();

// All done, create the connection entry in the VPN UI.
create();
}
33 changes: 33 additions & 0 deletions native_client_sdk/src/examples/api/vpn_provider/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head>
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<title>{{title}}</title>
<script type="text/javascript" src="common.js"></script>
<script type="text/javascript" src="example.js"></script>
</head>

<body {{attrs}}>
<h1>{{title}}</h1>
<h2>Status: <code id="statusField">NO-STATUS</code></h2>
<p>The VpnProvider example demonstrates how to use the VpnProvider API.<br/>
<i>This NaCl SDK example can only be used as an extension on Chrome OS.</i>
<br/>See the README file for detailed information on build and deploy.
</p>
<h3>Test</h3>
<p>After loading the extension to a Chromebook, open the 'VPN Provider' app.
<br/>Wait for the connection to be created.<br/> From the network
configuration menu select the 'Mock configuration' entry. <br/>
Observe the connection setup flow below.

</p>
<div id="listener"></div>
<div id="log" style="height: 400px; overflow-y: scroll;"></div>
</body>
</html>
137 changes: 137 additions & 0 deletions native_client_sdk/src/examples/api/vpn_provider/vpn_provider.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <queue>
#include <sstream>
#include <string>

#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_dictionary.h"

#include "vpn_provider_helper.h"

class VpnProviderInstance : public pp::Instance {
public:
explicit VpnProviderInstance(PP_Instance instance)
: pp::Instance(instance), vpn_provider_helper_(this) {
vpn_provider_helper_.Init();
}

virtual ~VpnProviderInstance() {}

// Handles messages from Javascript
virtual void HandleMessage(const pp::Var& message) {
// Expecting only dictionary messages from JS.
if (!message.is_dictionary()) {
PostMessage(
"NaCl: VpnProviderInstance::HandleMessage: "
"Unexpected message. Not a dictionary.");
return;
}

// Message type defined by 'cmd' key.
pp::VarDictionary dict(message);

std::string command;
if (!GetStringFromMessage(dict, "cmd", &command)) {
return;
}
if (command == "bind") {
std::string name, id;
if (!GetStringFromMessage(dict, "name", &name) ||
!GetStringFromMessage(dict, "id", &id)) {
return;
}
PostMessage(
"NaCl: VpnProviderInstance::HandleMessage: "
"Bind request.");
vpn_provider_helper_.Bind(name, id);
return;
}

if (command == "connected") {
PostMessage(
"NaCl: VpnProviderInstance::HandleMessage: "
"Connect request.");

/* This is the place where the developer would establing the VPN
* connection. The response would usually contain configuration details
* for the tunnel obtained from the VPN implementation.
*
* Currently just signaling that is was executed succesfuly.
*/

pp::VarDictionary dict;
dict.Set("cmd", "setParameters");
PostMessage(dict);
return;
}

if (command == "disconnected") {
PostMessage(
"NaCl: VpnProviderInstance::HandleMessage: "
"Disconnect request.");

/* This is the place where the developer would disconnect from the VPN
* connection.
*/

return;
}

PostMessage(
"NaCl: VpnProviderInstance::HandleMessage: "
"Unexpected command.");
}

private:
// Helper function for HandleMessage
bool GetStringFromMessage(const pp::VarDictionary& dict,
const char* key,
std::string* value) {
if (!value)
return false;

pp::Var val = dict.Get(key);
if (val.is_undefined()) {
std::stringstream ss;
ss << "NaCl: VpnProviderInstance::HandleMessage: Malformed message. No '"
<< key << "' key.";
PostMessage(ss.str());
return false;
}
if (!val.is_string()) {
std::stringstream ss;
ss << "NaCl: VpnProviderInstance::HandleMessage: Malformed message. ";
ss << "Type for key '" << key << "' is not string";
PostMessage(ss.str());
return false;
}

*value = val.AsString();
return true;
}

VpnProviderHelper vpn_provider_helper_;
};

class VpnProviderModule : public pp::Module {
public:
VpnProviderModule() : pp::Module() {}
virtual ~VpnProviderModule() {}

virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new VpnProviderInstance(instance);
}
};

namespace pp {

Module* CreateModule() {
return new VpnProviderModule();
}

} // namespace pp
Loading

0 comments on commit bb921df

Please sign in to comment.