Merge commit '09987d0f9d32e860f1391bb9c75b799501e2d141' as 'Subtrees/websocket'

This commit is contained in:
Vinnie Falco
2013-06-20 17:15:11 -07:00
255 changed files with 50217 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
BOOST_LIBS=boost_system boost_thread boost_regex boost_random
include ../common.mk
LDFLAGS := $(LDFLAGS) -lpthread
chat_client: chat_client.o chat_client_handler.o
$(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS)
%.o: %.cpp
$(CXX) -c $(CFLAGS) -o $@ $^
# cleanup by removing generated files
#
.PHONY: clean
clean:
rm -f *.o chat_client

View File

@@ -0,0 +1,21 @@
## chat_client
##
Import('env')
Import('boostlibs')
Import('wslib')
Import('platform_libs')
localenv = env.Clone ()
sources = ["chat_client.cpp","chat_client_handler.cpp"]
LIBS = [wslib, platform_libs] + boostlibs(['system',
'date_time',
'regex',
'thread',
'random'])
prg = localenv.Program('chat_client', sources, LIBS = LIBS)
Return('prg')

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2011, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "chat_client_handler.hpp"
#include "../../src/roles/client.hpp"
#include "../../src/websocketpp.hpp"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>
using boost::asio::ip::tcp;
using websocketpp::client;
using namespace websocketchat;
int main(int argc, char* argv[]) {
std::string uri;
if (argc != 2) {
std::cout << "Usage: `chat_client ws_uri`" << std::endl;
} else {
uri = argv[1];
}
try {
chat_client_handler_ptr handler(new chat_client_handler());
client endpoint(handler);
client::connection_ptr con;
endpoint.alog().unset_level(websocketpp::log::alevel::ALL);
endpoint.elog().unset_level(websocketpp::log::elevel::ALL);
endpoint.elog().set_level(websocketpp::log::elevel::RERROR);
endpoint.elog().set_level(websocketpp::log::elevel::FATAL);
con = endpoint.get_connection(uri);
con->add_request_header("User-Agent","WebSocket++/0.2.0 WebSocket++Chat/0.2.0");
con->add_subprotocol("com.zaphoyd.websocketpp.chat");
con->set_origin("http://zaphoyd.com");
endpoint.connect(con);
boost::thread t(boost::bind(&client::run, &endpoint, false));
char line[512];
while (std::cin.getline(line, 512)) {
handler->send(line);
}
t.join();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}

View File

@@ -0,0 +1,177 @@
<!doctype html>
<html>
<head>
<script type="text/javascript" src="vendor/jquery-1.6.3.min.js"></script>
</head>
<body>
<script type="text/javascript">
var ws;
var url;
$(document).ready(init);
function init() {
$(document).keypress(function(event) {
if ( event.which == 13 ) {
event.preventDefault();
send();
}
});
}
function connect() {
url = $("#server_url").val();
console.log(url);
if ("WebSocket" in window) {
ws = new WebSocket(url);
} else if ("MozWebSocket" in window) {
ws = new MozWebSocket(url);
} else {
chat_message("This Browser does not support WebSockets");
return;
}
ws.onopen = function(e) {
chat_message("A connection to "+url+" has been opened.");
$("#server_url").attr("disabled",true);
$("#toggle_connect").html("Disconnect");
};
ws.onerror = function(e) {
chat_message("An error occured, see console log for more details.");
console.log(e);
};
ws.onclose = function(e) {
chat_message("The connection to "+url+" was closed.");
};
ws.onmessage = function(e) {
var message = JSON.parse(e.data);
if (message.type == "msg") {
chat_message(message.value,message.sender);
} else if (message.type == "participants") {
var o = "<ul>";
for (var p in message.value) {
o += "<li>"+message.value[p]+"</li>";
}
o += "</ul>";
$("#participants").html(o);
}
};
}
function chat_message(message,sender) {
if (arguments.length == 1) {
sender = "";
}
var style;
if (sender == "") {
style = "client";
} else if (sender == "server") {
style = "server";
sender = "["+sender+"]";
} else {
style = "message";
sender = "["+sender+"]";
}
$("#messages").append("<span class='"+style+"'><span class='sender'>"+sender+"</span> <span class='msg'>"+message+"</span></span><br />");
$("#messages").prop({ scrollTop: $("#messages").prop("scrollHeight") });
}
function disconnect() {
ws.close();
$("#server_url").removeAttr("disabled");
$("#toggle_connect").html("Connect");
$("#participants").html("");
}
function toggle_connect() {
if ($("#server_url").attr("disabled") != "disabled") {
connect();
} else {
disconnect();
}
}
function send() {
if (ws === undefined || ws.readyState != 1) {
chat_message("Websocket is not avaliable for writing");
return;
}
ws.send($("#msg").val());
$("#msg").val("");
}
</script>
<style>
body,html {
margin: 0px;
padding: 0px;
height: 100%;
background-color: #999;
font-family: sans-serif;
font-size: 14px;
}
h3 {
}
#controls {
padding: 4px;
float:right;
width: 300px;
}
input {
width: 200px;
}
#messages {
height: 100%;
overflow: auto;
background-color: black;
}
#messages .client {
color: #ccc;
}
#messages .server {
color: yellow;
}
#messages .message {
color: white;
}
</style>
<div id="controls">
<div id="server">
<input type="text" name="server_url" id="server_url" value="ws://thor-websocket.zaphoyd.net:9000/chat" />
<button id="toggle_connect" onclick="toggle_connect();">Connect</button>
</div>
<div id="message_input"><input type="text" name="msg" id="msg" value="Hello World!" />
<button onclick="send();">Send</button></div>
<h3>Chat Participants</h3>
<div id="participants"></div>
</div>
<div id="messages"></div>
</body>
</html>

View File

@@ -0,0 +1,154 @@
/*
* Copyright (c) 2011, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "chat_client_handler.hpp"
#include <boost/algorithm/string/replace.hpp>
using websocketchat::chat_client_handler;
using websocketpp::client;
void chat_client_handler::on_fail(connection_ptr con) {
std::cout << "Connection failed" << std::endl;
}
void chat_client_handler::on_open(connection_ptr con) {
m_con = con;
std::cout << "Successfully connected" << std::endl;
}
void chat_client_handler::on_close(connection_ptr con) {
m_con = connection_ptr();
std::cout << "client was disconnected" << std::endl;
}
void chat_client_handler::on_message(connection_ptr con,message_ptr msg) {
decode_server_msg(msg->get_payload());
}
// CLIENT API
// client api methods will be called from outside the io_service.run thread
// they need to be careful to not touch unsyncronized member variables.
void chat_client_handler::send(const std::string &msg) {
if (!m_con) {
std::cerr << "Error: no connected session" << std::endl;
return;
}
if (msg == "/list") {
std::cout << "list all participants" << std::endl;
} else if (msg == "/close") {
close();
} else {
m_con->send(msg);
}
}
void chat_client_handler::close() {
if (!m_con) {
std::cerr << "Error: no connected session" << std::endl;
return;
}
m_con->close(websocketpp::close::status::GOING_AWAY,"");
}
// END CLIENT API
// {"type":"participants","value":[<participant>,...]}
// {"type":"msg","sender":"<sender>","value":"<msg>" }
void chat_client_handler::decode_server_msg(const std::string &msg) {
// for messages of type participants, erase and rebuild m_participants
// for messages of type msg, print out message
// NOTE: The chat server was written with the intention of the client having a built in
// JSON parser. To keep external dependencies low for this demonstration chat client I am
// parsing the server messages by hand.
std::string::size_type start = 9;
std::string::size_type end;
if (msg.substr(0,start) != "{\"type\":\"") {
// ignore
std::cout << "invalid message" << std::endl;
return;
}
if (msg.substr(start,15) == "msg\",\"sender\":\"") {
// parse message
std::string sender;
std::string message;
start += 15;
end = msg.find("\"",start);
while (end != std::string::npos) {
if (msg[end-1] == '\\') {
sender += msg.substr(start,end-start-1) + "\"";
start = end+1;
end = msg.find("\"",start);
} else {
sender += msg.substr(start,end-start);
start = end;
break;
}
}
if (msg.substr(start,11) != "\",\"value\":\"") {
std::cout << "invalid message" << std::endl;
return;
}
start += 11;
end = msg.find("\"",start);
while (end != std::string::npos) {
if (msg[end-1] == '\\') {
message += msg.substr(start,end-start-1) + "\"";
start = end+1;
end = msg.find("\"",start);
} else {
message += msg.substr(start,end-start);
start = end;
break;
}
}
std::cout << "[" << sender << "] " << message << std::endl;
} else if (msg.substr(start,23) == "participants\",\"value\":[") {
// parse participants
std::cout << "participants message" << std::endl;
} else {
// unknown message type
std::cout << "unknown message" << std::endl;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2011, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef CHAT_CLIENT_HANDLER_HPP
#define CHAT_CLIENT_HANDLER_HPP
// com.zaphoyd.websocketpp.chat protocol
//
// client messages:
// alias [UTF8 text, 16 characters max]
// msg [UTF8 text]
//
// server messages:
// {"type":"msg","sender":"<sender>","value":"<msg>" }
// {"type":"participants","value":[<participant>,...]}
#include <boost/shared_ptr.hpp>
#include "../../src/roles/client.hpp"
#include "../../src/websocketpp.hpp"
#include <map>
#include <string>
#include <queue>
using websocketpp::client;
namespace websocketchat {
class chat_client_handler : public client::handler {
public:
chat_client_handler() {}
virtual ~chat_client_handler() {}
void on_fail(connection_ptr con);
// connection to chat room complete
void on_open(connection_ptr con);
// connection to chat room closed
void on_close(connection_ptr con);
// got a new message from server
void on_message(connection_ptr con, message_ptr msg);
// CLIENT API
void send(const std::string &msg);
void close();
private:
void decode_server_msg(const std::string &msg);
// list of other chat participants
std::set<std::string> m_participants;
std::queue<std::string> m_msg_queue;
connection_ptr m_con;
};
typedef boost::shared_ptr<chat_client_handler> chat_client_handler_ptr;
}
#endif // CHAT_CLIENT_HANDLER_HPP

File diff suppressed because one or more lines are too long