diff options
author | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 07:25:02 +0000 |
---|---|---|
committer | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 07:25:02 +0000 |
commit | 7a42d9fed5e3b2041105d276c8734affa06dc28b (patch) | |
tree | 344510afc0c38b4ef262465c8ce39631ca68ccc8 /native_client_sdk | |
parent | d8abf4b8c5efdcbe3ed923368f5d794137e2776a (diff) | |
download | chromium_src-7a42d9fed5e3b2041105d276c8734affa06dc28b.zip chromium_src-7a42d9fed5e3b2041105d276c8734affa06dc28b.tar.gz chromium_src-7a42d9fed5e3b2041105d276c8734affa06dc28b.tar.bz2 |
Add WebSocket example.
Adds a websocket example to the NaCl SDK.
1- Add example to buildbot script
2- Add to main make example
3- Add examples sources
4- Update index.html to describe the example
TBR= toyoshim@chromium.org
Reviewed originally as: http://codereview.chromium.org/9310023/
BUG=112780
TEST=manual test is available from websocket.html
Review URL: https://chromiumcodereview.appspot.com/9387022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122058 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rw-r--r-- | native_client_sdk/src/examples/Makefile | 2 | ||||
-rw-r--r-- | native_client_sdk/src/examples/index.html | 7 | ||||
-rw-r--r-- | native_client_sdk/src/examples/websocket/Makefile | 76 | ||||
-rw-r--r-- | native_client_sdk/src/examples/websocket/websocket.cc | 165 | ||||
-rw-r--r-- | native_client_sdk/src/examples/websocket/websocket.html | 104 | ||||
-rw-r--r-- | native_client_sdk/src/examples/websocket/websocket.nmf | 6 |
6 files changed, 359 insertions, 1 deletions
diff --git a/native_client_sdk/src/examples/Makefile b/native_client_sdk/src/examples/Makefile index 6f022d5..1c891c3 100644 --- a/native_client_sdk/src/examples/Makefile +++ b/native_client_sdk/src/examples/Makefile @@ -10,7 +10,7 @@ PROJECTS:=dlopen fullscreen_tumbler gamepad geturl hello_world_glibc PROJECTS+=hello_world_interactive hello_world_newlib input_events load_progress PROJECTS+=mouselock multithreaded_input_events pi_generator pong sine_synth -PROJECTS+=tumbler +PROJECTS+=tumbler websocket # Define the default target all: diff --git a/native_client_sdk/src/examples/index.html b/native_client_sdk/src/examples/index.html index a3d2a88..0ec82e0 100644 --- a/native_client_sdk/src/examples/index.html +++ b/native_client_sdk/src/examples/index.html @@ -162,6 +162,13 @@ mulithreading...</p></dd> <p>Teaching focus: Mouse lock, Full-screen</p> </dd> + <dt><a href="websocket/websocket.html">Websocket</a></dt> + <dd> The Websocket example demonstrates how to use the Websocket API. The + user first connects to the test server: ws://html5rocks.websocket.org/echo by + clicking on the 'Connect'' button. Then hitting Send' will cause the app to + send the message to the server and retrieve the reply. + <p>Teaching focus: Websockets</p> + </dd> </dl> </body> </html> diff --git a/native_client_sdk/src/examples/websocket/Makefile b/native_client_sdk/src/examples/websocket/Makefile new file mode 100644 index 0000000..50692d7 --- /dev/null +++ b/native_client_sdk/src/examples/websocket/Makefile @@ -0,0 +1,76 @@ +# Copyright (c) 2011 The Native Client Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# +# GNU Make based build file. For details on GNU Make see: +# http://www.gnu.org/software/make/manual/make.html +# + +# +# Project information +# +# These variables store project specific settings for the project name +# build flags, files to copy or install. In the examples it is typically +# only the list of sources and project name that will actually change and +# the rest of the makefile is boilerplate for defining build rules. +# +PROJECT:=websocket +LDFLAGS:=-lppapi_cpp -lppapi +CXX_SOURCES:=$(PROJECT).cc + + +# +# Get pepper directory for toolchain and includes. +# +# If PEPPER_ROOT is not set, then assume it can be found a two directories up, +# from the default example directory location. +# +THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST))) +NACL_SDK_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..) + +# Project Build flags +WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror +CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS) + +# +# Compute tool paths +# +# +OSNAME:=$(shell python $(NACL_SDK_ROOT)/tools/getos.py) +TC_PATH:=$(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_x86_newlib) +CXX:=$(TC_PATH)/bin/i686-nacl-g++ + +# +# Disable DOS PATH warning when using Cygwin based tools Windows +# +CYGWIN ?= nodosfilewarning +export CYGWIN + + +# Declare the ALL target first, to make the 'all' target the default build +all: $(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe + +# Define 32 bit compile and link rules for C++ sources +x86_32_OBJS:=$(patsubst %.cc,%_32.o,$(CXX_SOURCES)) +$(x86_32_OBJS) : %_32.o : %.cc $(THIS_MAKE) + $(CXX) -o $@ -c $< -m32 -O0 -g $(CXXFLAGS) + +$(PROJECT)_x86_32.nexe : $(x86_32_OBJS) + $(CXX) -o $@ $^ -m32 -O0 -g $(CXXFLAGS) $(LDFLAGS) + +# Define 64 bit compile and link rules for C++ sources +x86_64_OBJS:=$(patsubst %.cc,%_64.o,$(CXX_SOURCES)) +$(x86_64_OBJS) : %_64.o : %.cc $(THIS_MAKE) + $(CXX) -o $@ -c $< -m64 -O0 -g $(CXXFLAGS) + +$(PROJECT)_x86_64.nexe : $(x86_64_OBJS) + $(CXX) -o $@ $^ -m64 -O0 -g $(CXXFLAGS) $(LDFLAGS) + + +# Define a phony rule so it always runs, to build nexe and start up server. +.PHONY: RUN +RUN: all + python ../httpd.py + + diff --git a/native_client_sdk/src/examples/websocket/websocket.cc b/native_client_sdk/src/examples/websocket/websocket.cc new file mode 100644 index 0000000..87692e1 --- /dev/null +++ b/native_client_sdk/src/examples/websocket/websocket.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2012 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 "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/var.h" +#include "ppapi/cpp/var_array_buffer.h" +#include "ppapi/cpp/websocket.h" + +class WebSocketInstance : public pp::Instance { + public: + explicit WebSocketInstance(PP_Instance instance) + : pp::Instance(instance), + websocket_(NULL) {} + virtual ~WebSocketInstance() {} + virtual void HandleMessage(const pp::Var& var_message); + + private: + bool IsConnected(); + + void Open(const std::string& url); + void Close(); + void Send(const std::string& message); + void Receive(); + + void OnConnectCompletion(int32_t result); + void OnCloseCompletion(int32_t result); + void OnReceiveCompletion(int32_t result); + + static void OnConnectCompletionCallback(void* user_data, int32_t result); + static void OnCloseCompletionCallback(void* user_data, int32_t result); + static void OnReceiveCompletionCallback(void* user_data, int32_t result); + + pp::WebSocket* websocket_; + pp::Var receive_var_; +}; + +void WebSocketInstance::HandleMessage(const pp::Var& var_message) { + if (!var_message.is_string()) + return; + std::string message = var_message.AsString(); + // This message must contain a command character followed by ';' and + // arguments like "X;arguments". + if (message.length() < 2 || message[1] != ';') + return; + switch (message[0]) { + case 'o': + // The command 'o' requests to open the specified URL. + // URL is passed as an argument like "o;URL". + Open(message.substr(2)); + break; + case 'c': + // The command 'c' requests to close without any argument like "c;" + Close(); + break; + case 's': + // The command 's' requests to send a message as a text frame. The message + // is passed as an argument like "s;message". + Send(message.substr(2)); + break; + } +} + +bool WebSocketInstance::IsConnected() { + if (!websocket_) + return false; + if (websocket_->GetReadyState() != PP_WEBSOCKETREADYSTATE_OPEN) + return false; + return true; +} + +void WebSocketInstance::Open(const std::string& url) { + pp::CompletionCallback callback(OnConnectCompletionCallback, this); + websocket_ = new pp::WebSocket(this); + if (!websocket_) + return; + websocket_->Connect(pp::Var(url), NULL, 0, callback); + PostMessage(pp::Var("connecting...")); +} + +void WebSocketInstance::Close() { + if (!IsConnected()) + return; + pp::CompletionCallback callback(OnCloseCompletionCallback, this); + websocket_->Close( + PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var("bye"), callback); +} + +void WebSocketInstance::Send(const std::string& message) { + if (!IsConnected()) + return; + websocket_->SendMessage(pp::Var(message)); + PostMessage(pp::Var(std::string("send: ") + message)); +} + +void WebSocketInstance::Receive() { + pp::CompletionCallback callback(OnReceiveCompletionCallback, this); + // |receive_var_| must be valid until |callback| is invoked. + // Just use a member variable. + websocket_->ReceiveMessage(&receive_var_, callback); +} + +void WebSocketInstance::OnConnectCompletion(int32_t result) { + if (result != PP_OK) { + PostMessage(pp::Var("connection failed")); + return; + } + PostMessage(pp::Var("connected")); + Receive(); +} + +void WebSocketInstance::OnCloseCompletion(int32_t result) { + PostMessage(pp::Var(PP_OK == result ? "closed" : "abnormally closed")); +} + +void WebSocketInstance::OnReceiveCompletion(int32_t result) { + if (result == PP_OK) { + if (receive_var_.is_array_buffer()) + PostMessage(pp::Var("receive: binary data")); + else + PostMessage(pp::Var(std::string("receive: ") + receive_var_.AsString())); + } + Receive(); +} + +void WebSocketInstance::OnConnectCompletionCallback(void* user_data, + int32_t result) { + WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data); + instance->OnConnectCompletion(result); +} + +void WebSocketInstance::OnCloseCompletionCallback(void* user_data, + int32_t result) { + WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data); + instance->OnCloseCompletion(result); +} + +void WebSocketInstance::OnReceiveCompletionCallback(void* user_data, + int32_t result) { + WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data); + instance->OnReceiveCompletion(result); +} + +// The WebSocketModule provides an implementation of pp::Module that creates +// WebSocketInstance objects when invoked. +class WebSocketModule : public pp::Module { + public: + WebSocketModule() : pp::Module() {} + virtual ~WebSocketModule() {} + + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new WebSocketInstance(instance); + } +}; + + +// Implement the required pp::CreateModule function that creates our specific +// kind of Module. +namespace pp { +Module* CreateModule() { + return new WebSocketModule(); +} +} // namespace pp diff --git a/native_client_sdk/src/examples/websocket/websocket.html b/native_client_sdk/src/examples/websocket/websocket.html new file mode 100644 index 0000000..d2db565 --- /dev/null +++ b/native_client_sdk/src/examples/websocket/websocket.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<html> + <!-- + Copyright (c) 2012 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> + <title>WebSocket</title> + + <script type="text/javascript"> + websocket = null; // Global application object. + statusText = 'NO-STATUS'; + + // Indicate success when the NaCl module has loaded. + function moduleDidLoad() { + websocket = document.getElementById('websocket'); + updateStatus('SUCCESS'); + } + + // Handle a message coming from the NaCl module. + function handleMessage(message_event) { + console.log(message_event); + document.getElementById('log').value += message_event.data + '\n'; + } + + function pageDidLoad() { + if (websocket == null) { + updateStatus('LOADING...'); + } else { + updateStatus(); + } + } + + function doConnect() { + // Send a request message. See also websocket.cc for the request format. + websocket.postMessage('o;' + document.getElementById('url').value); + } + + function doSend() { + // Send a request message. See also websocket.cc for the request format. + websocket.postMessage( + 's;' + document.getElementById('message').value); + } + + function doClose() { + // Send a request message. See also websocket.cc for the request format. + websocket.postMessage('c;'); + } + + function updateStatus(opt_message) { + if (opt_message) + statusText = opt_message; + var statusField = document.getElementById('statusField'); + if (statusField) { + statusField.innerHTML = statusText; + } + } + </script> +</head> +<body onload="pageDidLoad()"> + +<h1>WebSocket API Example</h1> +<p> + <form action="#" onsubmit="doConnect(); return false;"> + <input type="text" id="url" + value="ws://html5rocks.websocket.org/echo?encoding=text" /> + <input type="submit" value="Connect" /> + </form> + + <form action="#" onsubmit="doSend(); return false;"> + <input type="text" id="message" value="hello" /> + <input type="submit" value="Send" /> + </form> + + <form action="#" onsubmit="doClose(); return false;"> + <input type="submit" value="Close" /> + </form> + + <textarea id="log" rows="10" cols="80"></textarea> + + <div id="listener"> + <script type="text/javascript"> + var listener = document.getElementById('listener') + listener.addEventListener('load', moduleDidLoad, true); + listener.addEventListener('message', handleMessage, true); + </script> + + <embed name="nacl_module" + id="websocket" + width=0 height=0 + src="websocket.nmf" + type="application/x-nacl" /> + </div> +</p> + +<p>Set a server URL, then push "Connect" button to establish a connection.</p> +<p>"Send" button sends text message on the left text area.</p> +<p>"Close" button closes the connection/</p> + +<h2>Status</h2> +<div id="statusField">NO-STATUS</div> +</body> +</html> diff --git a/native_client_sdk/src/examples/websocket/websocket.nmf b/native_client_sdk/src/examples/websocket/websocket.nmf new file mode 100644 index 0000000..189344a --- /dev/null +++ b/native_client_sdk/src/examples/websocket/websocket.nmf @@ -0,0 +1,6 @@ +{ + "program": { + "x86-64": {"url": "websocket_x86_64.nexe"}, + "x86-32": {"url": "websocket_x86_32.nexe"} + } +} |