refectors session into client_session/server_session/core, adds client handler and chat_client example. Client functionality is experimental and non-compliant at the moment.
This commit is contained in:
1 parent
7d938df15e
commit
da1795feac
29 files changed
+2100
-452
No files matched your search
@@ -28,7 +28,7 @@
|
||||
# It's authors were Jonathan Wallace and Bernhard Fluehmann.
|
||||
|
||||
|
||||
objects = websocket_session.o websocket_server.o websocket_frame.o \
|
||||
objects = websocket_server_session.o websocket_client_session.o websocket_session.o websocket_server.o websocket_client.o websocket_frame.o \
|
||||
network_utilities.o sha1.o base64.o
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
CFLAGS = -O2
|
||||
LDFLAGS =
|
||||
|
||||
CXX ?= c++
|
||||
SHARED ?= "1"
|
||||
|
||||
ifeq ($(SHARED), 1)
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -lboost_thread -lboost_date_time -lwebsocketpp
|
||||
else
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -lboost_thread -lboost_date_time ../../libwebsocketpp.a
|
||||
endif
|
||||
|
||||
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
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 <websocketpp/websocketpp.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
using namespace websocketchat;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string url;
|
||||
|
||||
if (argc != 2) {
|
||||
std::cout << "Usage: `chat_client ws_url`" << std::endl;
|
||||
} else {
|
||||
url = argv[1];
|
||||
}
|
||||
|
||||
chat_client_handler_ptr c(new chat_client_handler());
|
||||
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
websocketpp::client_ptr client(new websocketpp::client(io_service,c));
|
||||
|
||||
client->init();
|
||||
|
||||
client->set_header("User Agent","WebSocket++/2011-09-25");
|
||||
client->add_subprotocol("com.zaphoyd.websocketpp.chat");
|
||||
|
||||
client->set_origin("http://zaphoyd.com");
|
||||
|
||||
client->connect(url);
|
||||
|
||||
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
|
||||
|
||||
char line[512];
|
||||
while (std::cin.getline(line, 512)) {
|
||||
c->send(line);
|
||||
}
|
||||
|
||||
t.join();
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="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>
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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_session_ptr;
|
||||
|
||||
void chat_client_handler::on_open(session_ptr s) {
|
||||
// not sure if anything needs to happen here.
|
||||
m_session = s;
|
||||
|
||||
std::cout << "Successfully connected" << std::endl;
|
||||
}
|
||||
|
||||
void chat_client_handler::on_close(session_ptr s,uint16_t status,const std::string &reason) {
|
||||
// not sure if anything needs to happen here either.
|
||||
|
||||
m_session = client_session_ptr();
|
||||
|
||||
std::cout << "client was disconnected" << std::endl;
|
||||
}
|
||||
|
||||
void chat_client_handler::on_message(session_ptr s,const std::string &msg) {
|
||||
std::cout << "message from server: " << msg << std::endl;
|
||||
|
||||
decode_server_msg(msg);
|
||||
}
|
||||
|
||||
// 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_session) {
|
||||
std::cerr << "Error: no connected session" << std::endl;
|
||||
return;
|
||||
}
|
||||
m_session->io_service().post(boost::bind(&chat_client_handler::do_send, this, msg));
|
||||
}
|
||||
|
||||
void chat_client_handler::close() {
|
||||
if (!m_session) {
|
||||
std::cerr << "Error: no connected session" << std::endl;
|
||||
return;
|
||||
}
|
||||
m_session->io_service().post(boost::bind(&chat_client_handler::do_close,this));
|
||||
}
|
||||
|
||||
// END CLIENT API
|
||||
|
||||
void chat_client_handler::do_send(const std::string &msg) {
|
||||
if (!m_session) {
|
||||
std::cerr << "Error: no connected session" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// check for local commands
|
||||
if (msg == "list") {
|
||||
std::cout << "list all participants" << std::endl;
|
||||
} else if (msg == "close") {
|
||||
do_close();
|
||||
} else {
|
||||
m_session->send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void chat_client_handler::do_close() {
|
||||
if (!m_session) {
|
||||
std::cerr << "Error: no connected session" << std::endl;
|
||||
return;
|
||||
}
|
||||
m_session->close(websocketpp::session::CLOSE_STATUS_GOING_AWAY,"");
|
||||
}
|
||||
|
||||
// {"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
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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/websocketpp.hpp"
|
||||
#include "../../src/websocket_connection_handler.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
|
||||
using websocketpp::session_ptr;
|
||||
|
||||
namespace websocketchat {
|
||||
|
||||
class chat_client_handler : public websocketpp::connection_handler {
|
||||
public:
|
||||
chat_client_handler() {}
|
||||
virtual ~chat_client_handler() {}
|
||||
|
||||
// ignored for clients?
|
||||
void validate(session_ptr s) {}
|
||||
|
||||
// connection to chat room complete
|
||||
void on_open(session_ptr s);
|
||||
|
||||
// connection to chat room closed
|
||||
void on_close(session_ptr s,uint16_t status,const std::string &reason);
|
||||
|
||||
// got a new message from server
|
||||
void on_message(session_ptr s,const std::string &msg);
|
||||
|
||||
// ignore messages
|
||||
void on_message(session_ptr s,const std::vector<unsigned char> &data) {}
|
||||
|
||||
// CLIENT API
|
||||
void send(const std::string &msg);
|
||||
void close();
|
||||
|
||||
private:
|
||||
// Client API internal
|
||||
void do_send(const std::string &msg);
|
||||
void do_close();
|
||||
|
||||
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;
|
||||
session_ptr m_session;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_client_handler> chat_client_handler_ptr;
|
||||
|
||||
}
|
||||
#endif // CHAT_CLIENT_HANDLER_HPP
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/*! jQuery v1.6.3 http://jquery.com/ | http://jquery.org/license */
|
||||
(function(a,b){function cu(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cr(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cq(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cp(){cn=b}function co(){setTimeout(cp,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bA.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bW(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bP,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bW(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bW(a,c,d,e,"*",g));return l}function bV(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bL),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function by(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bt:bu;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bv(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bl(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bd,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bk(a){f.nodeName(a,"input")?bj(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bj)}function bj(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bi(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bh(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bg(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bf(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function V(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(Q.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return fLine truncated
|
||||
,delete t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,M(a.origType,a.selector),f.extend({},a,{handler:L,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,M(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?D:C):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=D;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=D;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=D,this.stopPropagation()},isDefaultPrevented:C,isPropagationStopped:C,isImmediatePropagationStopped:C};var E=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},F=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?F:E,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?F:E)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.addLine truncated
|
||||
return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bh(a,d),e=bi(a),g=bi(d);for(h=0;e[h];++h)g[h]&&bh(e[h],g[h])}if(b){bg(a,d);if(c){e=bi(a),g=bi(d);for(h=0;e[h];++h)bg(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=be[l]||be._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bk(k[i]);else bk(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bc.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bm=/alpha\([^)]*\)/i,bn=/opacity=([^)]*)/,bo=/([A-Z]|^ms)/g,bp=/^-?\d+(?:px)?$/i,bq=/^-?\d/,br=/^([\-+])=([\-+.\de]+)/,bs={position:"absolute",visibility:"hidden",display:"block"},bt=["Left","Right"],bu=["Top","Bottom"],bv,bw,bx;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bv(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=br.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bv)return bv(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return by(a,b,d);f.swap(a,bs,function(){e=by(a,b,d)});return e}},set:function(a,b){if(!bp.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bn.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bm,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bm.test(g)?g.replace(bm,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bv(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bw=function(a,c){var d,e,g;c=c.replace(bo,"-Line truncated
|
||||
@@ -5,9 +5,9 @@ CXX ?= c++
|
||||
SHARED ?= "1"
|
||||
|
||||
ifeq ($(SHARED), 1)
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -lwebsocketpp
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -boost_date_time -lwebsocketpp
|
||||
else
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system ../../libwebsocketpp.a
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time ../../libwebsocketpp.a
|
||||
endif
|
||||
|
||||
chat_server: chat_server.o chat.o
|
||||
|
||||
@@ -29,15 +29,15 @@
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
using websocketchat::chat_handler;
|
||||
using websocketchat::chat_server_handler;
|
||||
using websocketpp::session_ptr;
|
||||
|
||||
void chat_handler::validate(session_ptr client) {
|
||||
void chat_server_handler::validate(session_ptr client) {
|
||||
std::stringstream err;
|
||||
|
||||
// We only know about the chat resource
|
||||
if (client->get_request() != "/chat") {
|
||||
err << "Request for unknown resource " << client->get_request();
|
||||
if (client->get_resource() != "/chat") {
|
||||
err << "Request for unknown resource " << client->get_resource();
|
||||
throw(websocketpp::handshake_error(err.str(),404));
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ void chat_handler::validate(session_ptr client) {
|
||||
}
|
||||
|
||||
|
||||
void chat_handler::connect(session_ptr client) {
|
||||
void chat_server_handler::on_open(session_ptr client) {
|
||||
std::cout << "client " << client << " joined the lobby." << std::endl;
|
||||
m_connections.insert(std::pair<session_ptr,std::string>(client,get_con_id(client)));
|
||||
|
||||
@@ -59,7 +59,7 @@ void chat_handler::connect(session_ptr client) {
|
||||
send_to_all(encode_message("server",m_connections[client]+" has joined the chat."));
|
||||
}
|
||||
|
||||
void chat_handler::disconnect(session_ptr client,uint16_t status,const std::string &reason) {
|
||||
void chat_server_handler::on_close(session_ptr client,uint16_t status,const std::string &reason) {
|
||||
std::map<session_ptr,std::string>::iterator it = m_connections.find(client);
|
||||
|
||||
if (it == m_connections.end()) {
|
||||
@@ -80,7 +80,7 @@ void chat_handler::disconnect(session_ptr client,uint16_t status,const std::stri
|
||||
send_to_all(encode_message("server",alias+" has left the chat."));
|
||||
}
|
||||
|
||||
void chat_handler::message(session_ptr client,const std::string &msg) {
|
||||
void chat_server_handler::on_message(session_ptr client,const std::string &msg) {
|
||||
std::cout << "message from client " << client << ": " << msg << std::endl;
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ void chat_handler::message(session_ptr client,const std::string &msg) {
|
||||
}
|
||||
|
||||
// {"type":"participants","value":[<participant>,...]}
|
||||
std::string chat_handler::serialize_state() {
|
||||
std::string chat_server_handler::serialize_state() {
|
||||
std::stringstream s;
|
||||
|
||||
s << "{\"type\":\"participants\",\"value\":[";
|
||||
@@ -147,7 +147,7 @@ std::string chat_handler::serialize_state() {
|
||||
}
|
||||
|
||||
// {"type":"msg","sender":"<sender>","value":"<msg>" }
|
||||
std::string chat_handler::encode_message(std::string sender,std::string msg,bool escape) {
|
||||
std::string chat_server_handler::encode_message(std::string sender,std::string msg,bool escape) {
|
||||
std::stringstream s;
|
||||
|
||||
// escape JSON characters
|
||||
@@ -167,13 +167,13 @@ std::string chat_handler::encode_message(std::string sender,std::string msg,bool
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::string chat_handler::get_con_id(session_ptr s) {
|
||||
std::string chat_server_handler::get_con_id(session_ptr s) {
|
||||
std::stringstream endpoint;
|
||||
endpoint << s->socket().remote_endpoint();
|
||||
return endpoint.str();
|
||||
}
|
||||
|
||||
void chat_handler::send_to_all(std::string data) {
|
||||
void chat_server_handler::send_to_all(std::string data) {
|
||||
std::map<session_ptr,std::string>::iterator it;
|
||||
for (it = m_connections.begin(); it != m_connections.end(); it++) {
|
||||
(*it).first->send(data);
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
// {"type":"participants","value":[<participant>,...]}
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "../../src/websocketpp.hpp"
|
||||
#include "../../src/websocket_connection_handler.hpp"
|
||||
|
||||
#include <map>
|
||||
@@ -48,23 +48,23 @@
|
||||
|
||||
namespace websocketchat {
|
||||
|
||||
class chat_handler : public websocketpp::connection_handler {
|
||||
class chat_server_handler : public websocketpp::connection_handler {
|
||||
public:
|
||||
chat_handler() {}
|
||||
virtual ~chat_handler() {}
|
||||
chat_server_handler() {}
|
||||
virtual ~chat_server_handler() {}
|
||||
|
||||
void validate(websocketpp::session_ptr client);
|
||||
|
||||
// add new connection to the lobby
|
||||
void connect(websocketpp::session_ptr client);
|
||||
void on_open(websocketpp::session_ptr client);
|
||||
|
||||
// someone disconnected from the lobby, remove them
|
||||
void disconnect(websocketpp::session_ptr client,uint16_t status,const std::string &reason);
|
||||
void on_close(websocketpp::session_ptr client,uint16_t status,const std::string &reason);
|
||||
|
||||
void message(websocketpp::session_ptr client,const std::string &msg);
|
||||
void on_message(websocketpp::session_ptr client,const std::string &msg);
|
||||
|
||||
// lobby will ignore binary messages
|
||||
void message(websocketpp::session_ptr client,
|
||||
void on_message(websocketpp::session_ptr client,
|
||||
const std::vector<unsigned char> &data) {}
|
||||
private:
|
||||
std::string serialize_state();
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
std::map<websocketpp::session_ptr,std::string> m_connections;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_handler> chat_handler_ptr;
|
||||
typedef boost::shared_ptr<chat_server_handler> chat_server_handler_ptr;
|
||||
|
||||
}
|
||||
#endif // CHAT_HPP
|
||||
@@ -27,29 +27,32 @@
|
||||
|
||||
#include "chat.hpp"
|
||||
|
||||
#include <websocketpp/websocket_server.hpp>
|
||||
#include "../../src/websocketpp.hpp"
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
using namespace websocketchat;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string host = "localhost:9000";
|
||||
short port = 9000;
|
||||
std::string host = "localhost";
|
||||
short port = 9003;
|
||||
std::string full_host;
|
||||
|
||||
if (argc == 3) {
|
||||
// TODO: input validation?
|
||||
host = argv[1];
|
||||
port = atoi(argv[2]);
|
||||
|
||||
|
||||
std::stringstream temp;
|
||||
temp << argv[1] << ":" << port;
|
||||
|
||||
host = temp.str();
|
||||
}
|
||||
|
||||
std::stringstream temp;
|
||||
|
||||
websocketchat::chat_handler_ptr chat_handler(new websocketchat::chat_handler());
|
||||
temp << host << ":" << port;
|
||||
full_host = temp.str();
|
||||
|
||||
|
||||
chat_server_handler_ptr chat_handler(new chat_server_handler());
|
||||
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
@@ -61,7 +64,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// setup server settings
|
||||
server->add_host(host);
|
||||
|
||||
server->add_host(full_host);
|
||||
// Chat server should only be receiving small text messages, reduce max
|
||||
// message size limit slightly to save memory, improve performance, and
|
||||
// guard against DoS attacks.
|
||||
@@ -70,7 +73,7 @@ int main(int argc, char* argv[]) {
|
||||
// start the server
|
||||
server->start_accept();
|
||||
|
||||
std::cout << "Starting chat server on " << host << std::endl;
|
||||
std::cout << "Starting chat server on " << full_host << std::endl;
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception& e) {
|
||||
|
||||
@@ -27,15 +27,15 @@
|
||||
|
||||
#include "echo.hpp"
|
||||
|
||||
using websocketecho::echo_handler;
|
||||
using websocketecho::echo_server_handler;
|
||||
|
||||
void echo_handler::validate(websocketpp::session_ptr client) {}
|
||||
void echo_server_handler::validate(websocketpp::session_ptr client) {}
|
||||
|
||||
void echo_handler::message(websocketpp::session_ptr client, const std::string &msg) {
|
||||
void echo_server_handler::on_message(websocketpp::session_ptr client, const std::string &msg) {
|
||||
client->send(msg);
|
||||
}
|
||||
|
||||
void echo_handler::message(websocketpp::session_ptr client,
|
||||
void echo_server_handler::on_message(websocketpp::session_ptr client,
|
||||
const std::vector<unsigned char> &data) {
|
||||
client->send(data);
|
||||
}
|
||||
@@ -25,38 +25,40 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ECHO_HANDLER_HPP
|
||||
#define ECHO_HANDLER_HPP
|
||||
|
||||
#ifndef ECHO_SERVER_HANDLER_HPP
|
||||
#define ECHO_SERVER_HANDLER_HPP
|
||||
#include "../../src/websocketpp.hpp"
|
||||
#include "../../src/websocket_connection_handler.hpp"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using websocketpp::session_ptr;
|
||||
|
||||
namespace websocketecho {
|
||||
|
||||
class echo_handler : public websocketpp::connection_handler {
|
||||
class echo_server_handler : public websocketpp::connection_handler {
|
||||
public:
|
||||
echo_handler() {}
|
||||
virtual ~echo_handler() {}
|
||||
echo_server_handler() {}
|
||||
virtual ~echo_server_handler() {}
|
||||
|
||||
// The echo server allows all domains is protocol free.
|
||||
void validate(websocketpp::session_ptr client);
|
||||
void validate(session_ptr client);
|
||||
|
||||
// an echo server is stateless. The handler has no need to keep track of connected
|
||||
// clients.
|
||||
void connect(websocketpp::session_ptr client) {}
|
||||
void disconnect(websocketpp::session_ptr client,uint16_t status,const std::string &reason) {}
|
||||
// an echo server is stateless.
|
||||
// The handler has no need to keep track of connected clients.
|
||||
void on_open(session_ptr client) {}
|
||||
void on_close(session_ptr client,uint16_t status,const std::string &reason) {}
|
||||
|
||||
// both text and binary messages are echoed back to the sending client.
|
||||
void message(websocketpp::session_ptr client,const std::string &msg);
|
||||
void message(websocketpp::session_ptr client,
|
||||
void on_message(session_ptr client,const std::string &msg);
|
||||
void on_message(session_ptr client,
|
||||
const std::vector<unsigned char> &data);
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<echo_handler> echo_handler_ptr;
|
||||
typedef boost::shared_ptr<echo_server_handler> echo_server_handler_ptr;
|
||||
|
||||
}
|
||||
|
||||
#endif // ECHO_HANDLER_HPP
|
||||
#endif // ECHO_SERVER_HANDLER_HPP
|
||||
Binary file not shown.
@@ -36,7 +36,7 @@ using boost::asio::ip::tcp;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string host = "localhost";
|
||||
short port = 5000;
|
||||
short port = 9002;
|
||||
std::string full_host;
|
||||
|
||||
if (argc == 3) {
|
||||
@@ -47,10 +47,10 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
std::stringstream temp;
|
||||
|
||||
temp << argv[1] << ":" << port;
|
||||
temp << host << ":" << port;
|
||||
full_host = temp.str();
|
||||
|
||||
websocketecho::echo_handler_ptr echo_handler(new websocketecho::echo_handler());
|
||||
websocketecho::echo_server_handler_ptr echo_handler(new websocketecho::echo_server_handler());
|
||||
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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 "websocket_client.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using websocketpp::client;
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
client::client(boost::asio::io_service& io_service,connection_handler_ptr defc)
|
||||
: m_elog_level(LOG_ALL),
|
||||
m_alog_level(ALOG_ALL),
|
||||
m_state(CLIENT_STATE_NULL),
|
||||
m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE),
|
||||
m_io_service(io_service),
|
||||
m_resolver(io_service),
|
||||
m_def_con_handler(defc) {}
|
||||
|
||||
void client::init() {
|
||||
m_client_session = client_session_ptr(
|
||||
new client_session(
|
||||
shared_from_this(),
|
||||
m_io_service,
|
||||
m_def_con_handler
|
||||
)
|
||||
);
|
||||
m_state = CLIENT_STATE_INITIALIZED;
|
||||
}
|
||||
|
||||
void client::connect(const std::string& url) {
|
||||
if (m_state != CLIENT_STATE_INITIALIZED) {
|
||||
throw client_error("connect can only be called after init and before a connection has been established");
|
||||
}
|
||||
|
||||
m_client_session->set_url(url);
|
||||
|
||||
std::stringstream port;
|
||||
port << m_client_session->get_port();
|
||||
|
||||
|
||||
tcp::resolver::query query(m_client_session->get_host(),
|
||||
port.str());
|
||||
tcp::resolver::iterator iterator = m_resolver.resolve(query);
|
||||
|
||||
boost::asio::async_connect(m_client_session->socket(),
|
||||
iterator,boost::bind(&client::handle_connect,
|
||||
this,
|
||||
boost::asio::placeholders::error));
|
||||
m_state = CLIENT_STATE_CONNECTING;
|
||||
}
|
||||
|
||||
|
||||
void client::add_subprotocol(const std::string& p) {
|
||||
if (m_state != CLIENT_STATE_INITIALIZED) {
|
||||
throw client_error("add_protocol can only be called after init and before connect");
|
||||
}
|
||||
m_client_session->add_subprotocol(p);
|
||||
}
|
||||
|
||||
void client::set_header(const std::string& key,const std::string& val) {
|
||||
if (m_state != CLIENT_STATE_INITIALIZED) {
|
||||
throw client_error("set_header can only be called after init and before connect");
|
||||
}
|
||||
m_client_session->set_header(key,val);
|
||||
}
|
||||
|
||||
void client::set_origin(const std::string& val) {
|
||||
if (m_state != CLIENT_STATE_INITIALIZED) {
|
||||
throw client_error("set_origin can only be called after init and before connect");
|
||||
}
|
||||
m_client_session->set_origin(val);
|
||||
}
|
||||
|
||||
|
||||
void client::set_max_message_size(uint64_t val) {
|
||||
if (val > frame::PAYLOAD_64BIT_LIMIT) {
|
||||
std::stringstream err;
|
||||
err << "Invalid maximum message size: " << val;
|
||||
|
||||
// TODO: Figure out what the ideal error behavior for this method.
|
||||
// Options:
|
||||
// Throw exception
|
||||
// Log error and set value to maximum allowed
|
||||
// Log error and leave value at whatever it was before
|
||||
log(err.str(),LOG_WARN);
|
||||
//throw client_error(err.str());
|
||||
}
|
||||
m_max_message_size = val;
|
||||
}
|
||||
|
||||
bool client::test_elog_level(uint16_t level) {
|
||||
return (level >= m_elog_level);
|
||||
}
|
||||
void client::set_elog_level(uint16_t level) {
|
||||
std::stringstream msg;
|
||||
msg << "Error logging level changing from "
|
||||
<< m_elog_level << " to " << level;
|
||||
log(msg.str(),LOG_INFO);
|
||||
|
||||
m_elog_level = level;
|
||||
}
|
||||
bool client::test_alog_level(uint16_t level) {
|
||||
return (level & m_alog_level);
|
||||
}
|
||||
void client::set_alog_level(uint16_t level) {
|
||||
if (test_alog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::stringstream msg;
|
||||
msg << "Access logging level " << level << " being set";
|
||||
access_log(msg.str(),ALOG_INFO);
|
||||
|
||||
m_alog_level |= level;
|
||||
}
|
||||
void client::unset_alog_level(uint16_t level) {
|
||||
if (!test_alog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::stringstream msg;
|
||||
msg << "Access logging level " << level << " being unset";
|
||||
access_log(msg.str(),ALOG_INFO);
|
||||
|
||||
m_alog_level &= ~level;
|
||||
}
|
||||
|
||||
bool client::validate_message_size(uint64_t val) {
|
||||
if (val > m_max_message_size) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void client::log(std::string msg,uint16_t level) {
|
||||
if (!test_elog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::cerr << "[Error Log] "
|
||||
<< boost::posix_time::to_iso_extended_string(
|
||||
boost::posix_time::second_clock::local_time())
|
||||
<< " " << msg << std::endl;
|
||||
}
|
||||
void client::access_log(std::string msg,uint16_t level) {
|
||||
if (!test_alog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::cout << "[Access Log] "
|
||||
<< boost::posix_time::to_iso_extended_string(
|
||||
boost::posix_time::second_clock::local_time())
|
||||
<< " " << msg << std::endl;
|
||||
}
|
||||
|
||||
void client::handle_connect(const boost::system::error_code& error) {
|
||||
if (!error) {
|
||||
m_state = CLIENT_STATE_CONNECTED;
|
||||
m_client_session->on_connect();
|
||||
} else {
|
||||
std::stringstream err;
|
||||
err << "An error occurred while establishing a connection: " << error;
|
||||
|
||||
log(err.str(),LOG_ERROR);
|
||||
throw client_error(err.str());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 WEBSOCKET_CLIENT_HPP
|
||||
#define WEBSOCKET_CLIENT_HPP
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace websocketpp {
|
||||
class client;
|
||||
typedef boost::shared_ptr<client> client_ptr;
|
||||
}
|
||||
|
||||
#include "websocketpp.hpp"
|
||||
#include "websocket_client_session.hpp"
|
||||
#include "websocket_connection_handler.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
class client_error : public std::exception {
|
||||
public:
|
||||
client_error(const std::string& msg)
|
||||
: m_msg(msg) {}
|
||||
~client_error() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
private:
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
class client : public boost::enable_shared_from_this<client> {
|
||||
public:
|
||||
// System logging levels
|
||||
/* static const uint16_t LOG_ALL = 0;
|
||||
static const uint16_t LOG_DEBUG = 1;
|
||||
static const uint16_t LOG_INFO = 2;
|
||||
static const uint16_t LOG_WARN = 3;
|
||||
static const uint16_t LOG_ERROR = 4;
|
||||
static const uint16_t LOG_FATAL = 5;
|
||||
static const uint16_t LOG_OFF = 6;
|
||||
|
||||
// Access logging controls
|
||||
// Individual bits
|
||||
static const uint16_t ALOG_CONNECT = 0x1;
|
||||
static const uint16_t ALOG_DISCONNECT = 0x2;
|
||||
static const uint16_t ALOG_MISC_CONTROL = 0x4;
|
||||
static const uint16_t ALOG_FRAME = 0x8;
|
||||
static const uint16_t ALOG_MESSAGE = 0x10;
|
||||
static const uint16_t ALOG_INFO = 0x20;
|
||||
static const uint16_t ALOG_HANDSHAKE = 0x40;
|
||||
// Useful groups
|
||||
static const uint16_t ALOG_OFF = 0x0;
|
||||
static const uint16_t ALOG_CONTROL = ALOG_CONNECT
|
||||
& ALOG_DISCONNECT
|
||||
& ALOG_MISC_CONTROL;
|
||||
static const uint16_t ALOG_ALL = 0xFFFF;
|
||||
*/
|
||||
static const uint16_t CLIENT_STATE_NULL = 0;
|
||||
static const uint16_t CLIENT_STATE_INITIALIZED = 1;
|
||||
static const uint16_t CLIENT_STATE_CONNECTING = 2;
|
||||
static const uint16_t CLIENT_STATE_CONNECTED = 3;
|
||||
|
||||
client(boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc);
|
||||
|
||||
// INTERFACE FOR LOCAL APPLICATIONS
|
||||
|
||||
// initializes the session. Methods that affect the opening handshake
|
||||
// such as add_protocol and set_header must be called after init and
|
||||
// before connect.
|
||||
void init();
|
||||
|
||||
// starts the connection process. Should be called before
|
||||
// io_service.run(), connection process will not start until run() has
|
||||
// been called.
|
||||
void connect(const std::string& url);
|
||||
|
||||
// Adds a protocol to the opening handshake.
|
||||
// Must be called before connect
|
||||
void add_subprotocol(const std::string& p);
|
||||
|
||||
// Sets the value of the given HTTP header to be sent during the
|
||||
// opening handshake. Must be called before connect
|
||||
void set_header(const std::string& key,const std::string& val);
|
||||
|
||||
void set_origin(const std::string& val);
|
||||
|
||||
void set_max_message_size(uint64_t val);
|
||||
|
||||
// Test methods determine if a message of the given level should be
|
||||
// written. elog shows all values above the level set. alog shows only
|
||||
// the values explicitly set.
|
||||
bool test_elog_level(uint16_t level);
|
||||
void set_elog_level(uint16_t level);
|
||||
|
||||
bool test_alog_level(uint16_t level);
|
||||
void set_alog_level(uint16_t level);
|
||||
void unset_alog_level(uint16_t level);
|
||||
|
||||
// INTERFACE FOR SESSIONS
|
||||
|
||||
// Check if message size is within server's acceptable parameters
|
||||
bool validate_message_size(uint64_t val);
|
||||
|
||||
// write to the server's logs
|
||||
void log(std::string msg,uint16_t level = LOG_ERROR);
|
||||
void access_log(std::string msg,uint16_t level);
|
||||
private:
|
||||
// if no errors starts the session's read loop and returns to the
|
||||
// start_accept phase.
|
||||
void handle_connect(const boost::system::error_code& error);
|
||||
|
||||
private:
|
||||
uint16_t m_elog_level;
|
||||
uint16_t m_alog_level;
|
||||
|
||||
uint16_t m_state;
|
||||
|
||||
std::set<std::string> m_hosts;
|
||||
uint64_t m_max_message_size;
|
||||
boost::asio::io_service& m_io_service;
|
||||
tcp::resolver m_resolver;
|
||||
client_session_ptr m_client_session;
|
||||
connection_handler_ptr m_def_con_handler;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_CLIENT_HPP
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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 "websocketpp.hpp"
|
||||
#include "websocket_client_session.hpp"
|
||||
|
||||
#include "websocket_frame.hpp"
|
||||
#include "utf8_validator/utf8_validator.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using websocketpp::client_session;
|
||||
|
||||
client_session::client_session (client_ptr c,
|
||||
boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc)
|
||||
: session(io_service,defc),m_client(c) {}
|
||||
|
||||
void client_session::on_connect() {
|
||||
write_handshake();
|
||||
}
|
||||
|
||||
void client_session::set_url(const std::string& url) {
|
||||
// TODO: impliment
|
||||
// TODO: input validation
|
||||
|
||||
m_host = "thor-websocket.zaphoyd.net";
|
||||
m_port = 9003;
|
||||
m_resource = "/chat";
|
||||
|
||||
}
|
||||
|
||||
bool client_session::get_secure() const {
|
||||
return m_secure;
|
||||
}
|
||||
|
||||
std::string client_session::get_host() const{
|
||||
return m_host;
|
||||
}
|
||||
|
||||
uint16_t client_session::get_port() const {
|
||||
return m_port;
|
||||
}
|
||||
|
||||
void client_session::set_header(const std::string &key,const std::string &val) {
|
||||
// TODO: prevent use of reserved headers
|
||||
m_client_headers[key] = val;
|
||||
}
|
||||
|
||||
void client_session::set_origin(const std::string& val) {
|
||||
// TODO: input validation
|
||||
m_client_origin = val;
|
||||
}
|
||||
|
||||
void client_session::add_subprotocol(const std::string &val) {
|
||||
// TODO: input validation
|
||||
m_client_subprotocols.push_back(val);
|
||||
}
|
||||
|
||||
void client_session::add_extension(const std::string& val) {
|
||||
// TODO: input validation
|
||||
m_client_extensions.push_back(val);
|
||||
}
|
||||
|
||||
void client_session::read_handshake() {
|
||||
boost::asio::async_read_until(
|
||||
m_socket,
|
||||
m_buf,
|
||||
"\r\n\r\n",
|
||||
boost::bind(
|
||||
&session::handle_read_handshake,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void client_session::handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred) {
|
||||
// parse server handshake
|
||||
|
||||
|
||||
// read handshake and set local state (or pass to write_handshake)
|
||||
std::ostringstream line;
|
||||
line << &m_buf;
|
||||
m_raw_server_handshake += line.str();
|
||||
|
||||
m_client->access_log(m_raw_server_handshake,ALOG_HANDSHAKE);
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type end;
|
||||
|
||||
// Get request and parse headers
|
||||
end = m_raw_server_handshake.find("\r\n",start);
|
||||
|
||||
while(end != std::string::npos) {
|
||||
tokens.push_back(m_raw_server_handshake.substr(start, end - start));
|
||||
|
||||
start = end + 2;
|
||||
|
||||
end = m_raw_server_handshake.find("\r\n",start);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tokens.size(); i++) {
|
||||
if (i == 0) {
|
||||
m_server_http_request = tokens[i];
|
||||
}
|
||||
|
||||
end = tokens[i].find(": ",0);
|
||||
|
||||
if (end != std::string::npos) {
|
||||
std::string h = tokens[i].substr(0,end);
|
||||
if (get_server_header(h) == "") {
|
||||
m_server_headers[h] = tokens[i].substr(end+2);
|
||||
} else {
|
||||
m_server_headers[h] += ", " + tokens[i].substr(end+2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handshake error checking
|
||||
try {
|
||||
std::stringstream err;
|
||||
std::string h;
|
||||
|
||||
// TODO: allow versions greater than 1.1
|
||||
if (m_server_http_request.substr(0,9) != "HTTP/1.1 ") {
|
||||
err << "Websocket handshake has invalid HTTP version: "
|
||||
<< m_server_http_request.substr(0,9);
|
||||
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
// check the HTTP version
|
||||
if (m_server_http_request.substr(9,3) != "101") {
|
||||
err << "Websocket handshake ended with status "
|
||||
<< m_server_http_request.substr(9);
|
||||
|
||||
// TODO: check version header for other supported versions.
|
||||
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
// verify the presence of required headers
|
||||
h = get_server_header("Upgrade");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Upgrade header is missing",400));
|
||||
} else if (!boost::iequals(h,"websocket")) {
|
||||
err << "Upgrade header was " << h << " instead of \"websocket\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
h = get_server_header("Connection");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Connection header is missing",400));
|
||||
} else if (!boost::ifind_first(h,"upgrade")) {
|
||||
err << "Connection header, \"" << h
|
||||
<< "\", does not contain required token \"upgrade\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
if (get_server_header("Sec-WebSocket-Accept") == "") {
|
||||
throw(handshake_error("Required Sec-WebSocket-Key header is missing",400));
|
||||
} else {
|
||||
// TODO: make a helper function for this.
|
||||
std::string server_key = m_client_key;
|
||||
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
SHA1 sha;
|
||||
uint32_t message_digest[5];
|
||||
|
||||
sha.Reset();
|
||||
sha << server_key.c_str();
|
||||
|
||||
if (!sha.Result(message_digest)) {
|
||||
m_client->log("Error computing handshake sha1 hash.",LOG_ERROR);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
// convert sha1 hash bytes to network byte order because this sha1
|
||||
// library works on ints rather than bytes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
message_digest[i] = htonl(message_digest[i]);
|
||||
}
|
||||
|
||||
server_key = base64_encode(
|
||||
reinterpret_cast<const unsigned char*>(message_digest),20);
|
||||
if (server_key != get_server_header("Sec-WebSocket-Accept")) {
|
||||
m_client->log("Server key does not match",LOG_ERROR);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (const handshake_error& e) {
|
||||
std::stringstream err;
|
||||
err << "Caught handshake exception: " << e.what();
|
||||
|
||||
m_client->access_log(e.what(),ALOG_HANDSHAKE);
|
||||
m_client->log(err.str(),LOG_ERROR);
|
||||
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
log_open_result();
|
||||
|
||||
m_status = OPEN;
|
||||
|
||||
if (m_local_interface) {
|
||||
m_local_interface->on_open(shared_from_this());
|
||||
}
|
||||
|
||||
reset_message();
|
||||
read_frame();
|
||||
}
|
||||
|
||||
void client_session::write_handshake() {
|
||||
// generate client handshake.
|
||||
std::string client_handshake;
|
||||
|
||||
client_handshake += "GET "+m_resource+" HTTP/1.1\r\n";
|
||||
|
||||
set_header("Upgrade","websocket");
|
||||
set_header("Connection","Upgrade");
|
||||
set_header("Sec-WebSocket-Version","13");
|
||||
|
||||
set_header("Host",m_host);
|
||||
|
||||
if (m_client_origin != "") {
|
||||
set_header("Origin",m_client_origin);
|
||||
}
|
||||
|
||||
// TODO: generate proper key
|
||||
m_client_key = "XO4pxrIMLnK1CEVQP9untQ==";
|
||||
set_header("Sec-WebSocket-Key",m_client_key);
|
||||
|
||||
|
||||
|
||||
set_header("User Agent","WebSocket++/2011-09-25");
|
||||
|
||||
header_list::iterator it;
|
||||
for (it = m_client_headers.begin(); it != m_client_headers.end(); it++) {
|
||||
client_handshake += it->first + ": " + it->second + "\r\n";
|
||||
}
|
||||
|
||||
client_handshake += "\r\n";
|
||||
|
||||
m_raw_client_handshake = client_handshake;
|
||||
|
||||
// start async write to handle_write_handshake
|
||||
boost::asio::async_write(
|
||||
m_socket,
|
||||
boost::asio::buffer(m_raw_client_handshake),
|
||||
boost::bind(
|
||||
&session::handle_write_handshake,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void client_session::handle_write_handshake(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error writing handshake",error);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
read_handshake();
|
||||
}
|
||||
|
||||
void client_session::log(const std::string& msg, uint16_t level) const {
|
||||
m_client->log(msg,level);
|
||||
}
|
||||
|
||||
void client_session::access_log(const std::string& msg, uint16_t level) const {
|
||||
m_client->access_log(msg,level);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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 WEBSOCKET_CLIENT_SESSION_HPP
|
||||
#define WEBSOCKET_CLIENT_SESSION_HPP
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace websocketpp {
|
||||
class client_session;
|
||||
typedef boost::shared_ptr<client_session> client_session_ptr;
|
||||
}
|
||||
|
||||
#include "websocket_session.hpp"
|
||||
#include "websocket_client.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
class client_session : public session {
|
||||
public:
|
||||
client_session (client_ptr c,
|
||||
boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc);
|
||||
|
||||
/*** CLIENT INTERFACE ***/
|
||||
|
||||
// This function is called when a tcp connection has been established and
|
||||
// the connection is ready to start the opening handshake.
|
||||
void on_connect();
|
||||
|
||||
/*** HANDSHAKE INTERFACE ***/
|
||||
|
||||
void set_url(const std::string& url);
|
||||
|
||||
bool get_secure() const;
|
||||
std::string get_host() const;
|
||||
uint16_t get_port() const;
|
||||
|
||||
// Set an HTTP header for the outgoing client handshake.
|
||||
void set_header(const std::string& key,const std::string& val);
|
||||
|
||||
// adds a subprotocol. This will result in the appropriate
|
||||
// Sec-WebSocket-Protocol header being sent with the opening connection.
|
||||
// Values will be sent in the order they were added. Servers interpret this
|
||||
// order as the preferred order.
|
||||
void add_subprotocol(const std::string &val);
|
||||
|
||||
// Sets the origin value that will be sent to the server
|
||||
void set_origin(const std::string &val);
|
||||
|
||||
// Adds an extension to the extension list. Extensions are sent in the
|
||||
// order added
|
||||
void add_extension(const std::string& val);
|
||||
|
||||
/*** SESSION INTERFACE ***/
|
||||
// see session
|
||||
bool is_server() const {return false;}
|
||||
|
||||
void log(const std::string& msg, uint16_t level) const;
|
||||
void access_log(const std::string& msg, uint16_t level) const;
|
||||
protected:
|
||||
// Opening handshake processors and callbacks.
|
||||
virtual void write_handshake();
|
||||
virtual void handle_write_handshake(const boost::system::error_code& e);
|
||||
virtual void read_handshake();
|
||||
virtual void handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
private:
|
||||
|
||||
protected:
|
||||
// url parts
|
||||
bool m_secure;
|
||||
std::string m_host;
|
||||
uint16_t m_port;
|
||||
|
||||
// handshake stuff
|
||||
std::string m_client_key;
|
||||
|
||||
// connection resources
|
||||
client_ptr m_client;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_CLIENT_SESSION_HPP
|
||||
@@ -56,11 +56,14 @@ public:
|
||||
// client (useful for returning non-standard error codes)
|
||||
virtual void validate(session_ptr client) = 0;
|
||||
|
||||
|
||||
|
||||
// this will be called once the connected websocket is avaliable for
|
||||
// writing messages. client may be a new websocket session or an existing
|
||||
// session that was recently passed to this handler.
|
||||
virtual void connect(session_ptr client) = 0;
|
||||
virtual void on_open(session_ptr client) = 0;
|
||||
|
||||
|
||||
// this will be called when the connected websocket is no longer avaliable
|
||||
// for writing messages. This occurs under the following conditions:
|
||||
// - Disconnect message recieved from the remote endpoint
|
||||
@@ -69,15 +72,15 @@ public:
|
||||
// calls the disconnect method of session
|
||||
// - The connection handler assigned to this client was set to another
|
||||
// handler
|
||||
virtual void disconnect(session_ptr client,uint16_t status,const std::string &reason) = 0;
|
||||
virtual void on_close(session_ptr client,uint16_t status,const std::string &reason) = 0;
|
||||
|
||||
// this will be called when a text message is recieved. Text will be
|
||||
// encoded as UTF-8.
|
||||
virtual void message(session_ptr client,const std::string &msg) = 0;
|
||||
virtual void on_message(session_ptr client,const std::string &msg) = 0;
|
||||
|
||||
// this will be called when a binary message is recieved. Argument is a
|
||||
// vector of the raw bytes in the message body.
|
||||
virtual void message(session_ptr client,
|
||||
virtual void on_message(session_ptr client,
|
||||
const std::vector<unsigned char> &data) = 0;
|
||||
};
|
||||
|
||||
|
||||
+56
-12
@@ -37,7 +37,9 @@ using websocketpp::server;
|
||||
server::server(boost::asio::io_service& io_service,
|
||||
const tcp::endpoint& endpoint,
|
||||
connection_handler_ptr defc)
|
||||
: m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE),
|
||||
: m_elog_level(LOG_ALL),
|
||||
m_alog_level(ALOG_ALL),
|
||||
m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE),
|
||||
m_io_service(io_service),
|
||||
m_acceptor(io_service, endpoint),
|
||||
m_def_con_handler(defc) {}
|
||||
@@ -61,11 +63,47 @@ void server::set_max_message_size(uint64_t val) {
|
||||
// Throw exception
|
||||
// Log error and set value to maximum allowed
|
||||
// Log error and leave value at whatever it was before
|
||||
throw server_error(err.str());
|
||||
log(err.str(),LOG_WARN);
|
||||
//throw server_error(err.str());
|
||||
}
|
||||
m_max_message_size = val;
|
||||
}
|
||||
|
||||
bool server::test_elog_level(uint16_t level) {
|
||||
return (level >= m_elog_level);
|
||||
}
|
||||
void server::set_elog_level(uint16_t level) {
|
||||
std::stringstream msg;
|
||||
msg << "Error logging level changing from "
|
||||
<< m_elog_level << " to " << level;
|
||||
log(msg.str(),LOG_INFO);
|
||||
|
||||
m_elog_level = level;
|
||||
}
|
||||
bool server::test_alog_level(uint16_t level) {
|
||||
return (level & m_alog_level);
|
||||
}
|
||||
void server::set_alog_level(uint16_t level) {
|
||||
if (test_alog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::stringstream msg;
|
||||
msg << "Access logging level " << level << " being set";
|
||||
access_log(msg.str(),ALOG_INFO);
|
||||
|
||||
m_alog_level |= level;
|
||||
}
|
||||
void server::unset_alog_level(uint16_t level) {
|
||||
if (!test_alog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::stringstream msg;
|
||||
msg << "Access logging level " << level << " being unset";
|
||||
access_log(msg.str(),ALOG_INFO);
|
||||
|
||||
m_alog_level &= ~level;
|
||||
}
|
||||
|
||||
bool server::validate_host(std::string host) {
|
||||
if (m_hosts.find(host) == m_hosts.end()) {
|
||||
return false;
|
||||
@@ -80,13 +118,19 @@ bool server::validate_message_size(uint64_t val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void server::error_log(std::string msg) {
|
||||
void server::log(std::string msg,uint16_t level) {
|
||||
if (!test_elog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::cerr << "[Error Log] "
|
||||
<< boost::posix_time::to_iso_extended_string(
|
||||
boost::posix_time::second_clock::local_time())
|
||||
<< " " << msg << std::endl;
|
||||
}
|
||||
void server::access_log(std::string msg) {
|
||||
void server::access_log(std::string msg,uint16_t level) {
|
||||
if (!test_alog_level(level)) {
|
||||
return;
|
||||
}
|
||||
std::cout << "[Access Log] "
|
||||
<< boost::posix_time::to_iso_extended_string(
|
||||
boost::posix_time::second_clock::local_time())
|
||||
@@ -94,31 +138,31 @@ void server::access_log(std::string msg) {
|
||||
}
|
||||
|
||||
void server::start_accept() {
|
||||
session_ptr new_ws(new session(shared_from_this(),
|
||||
m_io_service,
|
||||
m_def_con_handler));
|
||||
server_session_ptr new_session(new server_session(shared_from_this(),
|
||||
m_io_service,
|
||||
m_def_con_handler));
|
||||
|
||||
m_acceptor.async_accept(
|
||||
new_ws->socket(),
|
||||
new_session->socket(),
|
||||
boost::bind(
|
||||
&server::handle_accept,
|
||||
this,
|
||||
new_ws,
|
||||
new_session,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void server::handle_accept(session_ptr session,
|
||||
void server::handle_accept(server_session_ptr session,
|
||||
const boost::system::error_code& error) {
|
||||
|
||||
if (!error) {
|
||||
session->start();
|
||||
session->on_connect();
|
||||
} else {
|
||||
std::stringstream err;
|
||||
err << "Error accepting socket connection: " << error;
|
||||
|
||||
error_log(err.str());
|
||||
log(err.str(),LOG_ERROR);
|
||||
throw server_error(err.str());
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace websocketpp {
|
||||
}
|
||||
|
||||
#include "websocketpp.hpp"
|
||||
#include "websocket_session.hpp"
|
||||
#include "websocket_server_session.hpp"
|
||||
#include "websocket_connection_handler.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
@@ -61,6 +61,31 @@ private:
|
||||
|
||||
class server : public boost::enable_shared_from_this<server> {
|
||||
public:
|
||||
// System logging levels
|
||||
/*static const uint16_t LOG_ALL = 0;
|
||||
static const uint16_t LOG_DEBUG = 1;
|
||||
static const uint16_t LOG_INFO = 2;
|
||||
static const uint16_t LOG_WARN = 3;
|
||||
static const uint16_t LOG_ERROR = 4;
|
||||
static const uint16_t LOG_FATAL = 5;
|
||||
static const uint16_t LOG_OFF = 6;
|
||||
|
||||
// Access logging controls
|
||||
// Individual bits
|
||||
static const uint16_t ALOG_CONNECT = 0x1;
|
||||
static const uint16_t ALOG_DISCONNECT = 0x2;
|
||||
static const uint16_t ALOG_MISC_CONTROL = 0x4;
|
||||
static const uint16_t ALOG_FRAME = 0x8;
|
||||
static const uint16_t ALOG_MESSAGE = 0x10;
|
||||
static const uint16_t ALOG_INFO = 0x20;
|
||||
static const uint16_t ALOG_HANDSHAKE = 0x40;
|
||||
// Useful groups
|
||||
static const uint16_t ALOG_OFF = 0x0;
|
||||
static const uint16_t ALOG_CONTROL = ALOG_CONNECT
|
||||
& ALOG_DISCONNECT
|
||||
& ALOG_MISC_CONTROL;
|
||||
static const uint16_t ALOG_ALL = 0xFFFF;
|
||||
*/
|
||||
server(boost::asio::io_service& io_service,
|
||||
const tcp::endpoint& endpoint,
|
||||
connection_handler_ptr defc);
|
||||
@@ -68,7 +93,9 @@ class server : public boost::enable_shared_from_this<server> {
|
||||
// creates a new session object and connects the next websocket
|
||||
// connection to it.
|
||||
void start_accept();
|
||||
|
||||
|
||||
// INTERFACE FOR LOCAL APPLICATIONS
|
||||
|
||||
// Add or remove a host string (host:port) to the list of acceptable
|
||||
// hosts to accept websocket connections from. Additions/deletions here
|
||||
// only affect new connections.
|
||||
@@ -77,6 +104,18 @@ class server : public boost::enable_shared_from_this<server> {
|
||||
|
||||
void set_max_message_size(uint64_t val);
|
||||
|
||||
// Test methods determine if a message of the given level should be
|
||||
// written. elog shows all values above the level set. alog shows only
|
||||
// the values explicitly set.
|
||||
bool test_elog_level(uint16_t level);
|
||||
void set_elog_level(uint16_t level);
|
||||
|
||||
bool test_alog_level(uint16_t level);
|
||||
void set_alog_level(uint16_t level);
|
||||
void unset_alog_level(uint16_t level);
|
||||
|
||||
// INTERFACE FOR SESSIONS
|
||||
|
||||
// Check if this server will respond to this host.
|
||||
bool validate_host(std::string host);
|
||||
|
||||
@@ -84,17 +123,18 @@ class server : public boost::enable_shared_from_this<server> {
|
||||
bool validate_message_size(uint64_t val);
|
||||
|
||||
// write to the server's logs
|
||||
void error_log(std::string msg);
|
||||
void access_log(std::string msg);
|
||||
void log(std::string msg,uint16_t level = LOG_ERROR);
|
||||
void access_log(std::string msg,uint16_t level);
|
||||
private:
|
||||
|
||||
|
||||
// if no errors starts the session's read loop and returns to the
|
||||
// start_accept phase.
|
||||
void handle_accept(session_ptr session,
|
||||
void handle_accept(server_session_ptr session,
|
||||
const boost::system::error_code& error);
|
||||
|
||||
private:
|
||||
uint16_t m_elog_level;
|
||||
uint16_t m_alog_level;
|
||||
|
||||
std::set<std::string> m_hosts;
|
||||
uint64_t m_max_message_size;
|
||||
boost::asio::io_service& m_io_service;
|
||||
@@ -104,4 +144,4 @@ class server : public boost::enable_shared_from_this<server> {
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_SERVER_HPP
|
||||
#endif // WEBSOCKET_SERVER_HPP
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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 "websocketpp.hpp"
|
||||
#include "websocket_server_session.hpp"
|
||||
|
||||
#include "websocket_frame.hpp"
|
||||
#include "utf8_validator/utf8_validator.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using websocketpp::server_session;
|
||||
|
||||
server_session::server_session(server_ptr s,
|
||||
boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc)
|
||||
: session(io_service,defc),m_server(s) {}
|
||||
|
||||
void server_session::on_connect() {
|
||||
read_handshake();
|
||||
}
|
||||
|
||||
|
||||
void server_session::set_header(const std::string &key,const std::string &val) {
|
||||
// TODO: prevent use of reserved headers;
|
||||
m_server_headers[key] = val;
|
||||
}
|
||||
|
||||
void server_session::select_subprotocol(const std::string& val) {
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_client_subprotocols.begin(),
|
||||
m_client_subprotocols.end(),
|
||||
val);
|
||||
|
||||
if (val != "" && it == m_client_subprotocols.end()) {
|
||||
throw server_error("Attempted to choose a subprotocol not proposed by the client");
|
||||
}
|
||||
|
||||
m_server_subprotocol = val;
|
||||
}
|
||||
|
||||
void server_session::select_extension(const std::string& val) {
|
||||
if (val == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_client_extensions.begin(),
|
||||
m_client_extensions.end(),
|
||||
val);
|
||||
|
||||
if (it == m_client_extensions.end()) {
|
||||
throw server_error("Attempted to choose an extension not proposed by the client");
|
||||
}
|
||||
|
||||
m_server_extensions.push_back(val);
|
||||
}
|
||||
|
||||
void server_session::read_handshake() {
|
||||
boost::asio::async_read_until(
|
||||
m_socket,
|
||||
m_buf,
|
||||
"\r\n\r\n",
|
||||
boost::bind(
|
||||
&session::handle_read_handshake,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void server_session::handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred) {
|
||||
std::ostringstream line;
|
||||
line << &m_buf;
|
||||
m_raw_client_handshake += line.str();
|
||||
|
||||
access_log(m_raw_client_handshake,ALOG_HANDSHAKE);
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type end;
|
||||
|
||||
// Get request and parse headers
|
||||
end = m_raw_client_handshake.find("\r\n",start);
|
||||
|
||||
while(end != std::string::npos) {
|
||||
tokens.push_back(m_raw_client_handshake.substr(start, end - start));
|
||||
|
||||
start = end + 2;
|
||||
|
||||
end = m_raw_client_handshake.find("\r\n",start);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tokens.size(); i++) {
|
||||
if (i == 0) {
|
||||
m_client_http_request = tokens[i];
|
||||
}
|
||||
|
||||
end = tokens[i].find(": ",0);
|
||||
|
||||
if (end != std::string::npos) {
|
||||
std::string h = tokens[i].substr(0,end);
|
||||
|
||||
if (get_client_header(h) == "") {
|
||||
m_client_headers[h] = tokens[i].substr(end+2);
|
||||
} else {
|
||||
m_client_headers[h] += ", " + tokens[i].substr(end+2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handshake error checking
|
||||
try {
|
||||
std::stringstream err;
|
||||
std::string h;
|
||||
|
||||
// check the method
|
||||
if (m_client_http_request.substr(0,4) != "GET ") {
|
||||
err << "Websocket handshake has invalid method: "
|
||||
<< m_client_http_request.substr(0,4);
|
||||
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
// check the HTTP version
|
||||
// TODO: allow versions greater than 1.1
|
||||
end = m_client_http_request.find(" HTTP/1.1",4);
|
||||
if (end == std::string::npos) {
|
||||
err << "Websocket handshake has invalid HTTP version";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
m_resource = m_client_http_request.substr(4,end-4);
|
||||
|
||||
// verify the presence of required headers
|
||||
h = get_client_header("Host");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Host header is missing",400));
|
||||
} else if (!m_server->validate_host(h)) {
|
||||
err << "Host " << h << " is not one of this server's names.";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
h = get_client_header("Upgrade");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Upgrade header is missing",400));
|
||||
} else if (!boost::iequals(h,"websocket")) {
|
||||
err << "Upgrade header was " << h << " instead of \"websocket\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
h = get_client_header("Connection");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Connection header is missing",400));
|
||||
} else if (!boost::ifind_first(h,"upgrade")) {
|
||||
err << "Connection header, \"" << h
|
||||
<< "\", does not contain required token \"upgrade\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
if (get_client_header("Sec-WebSocket-Key") == "") {
|
||||
throw(handshake_error("Required Sec-WebSocket-Key header is missing",400));
|
||||
}
|
||||
|
||||
h = get_client_header("Sec-WebSocket-Version");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Sec-WebSocket-Version header is missing",400));
|
||||
} else {
|
||||
m_version = atoi(h.c_str());
|
||||
|
||||
if (m_version != 7 && m_version != 8 && m_version != 13) {
|
||||
err << "This server doesn't support WebSocket protocol version "
|
||||
<< m_version;
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_version < 13) {
|
||||
h = get_client_header("Sec-WebSocket-Origin");
|
||||
} else {
|
||||
h = get_client_header("Origin");
|
||||
}
|
||||
|
||||
if (h != "") {
|
||||
m_client_origin = h;
|
||||
}
|
||||
|
||||
// TODO: extract subprotocols
|
||||
// TODO: extract extensions
|
||||
|
||||
// optional headers (delegated to the local interface)
|
||||
if (m_local_interface) {
|
||||
m_local_interface->validate(shared_from_this());
|
||||
}
|
||||
|
||||
m_server_http_code = 101;
|
||||
m_server_http_string = "Switching Protocols";
|
||||
} catch (const handshake_error& e) {
|
||||
std::stringstream err;
|
||||
err << "Caught handshake exception: " << e.what();
|
||||
|
||||
access_log(e.what(),ALOG_HANDSHAKE);
|
||||
log(err.str(),LOG_ERROR);
|
||||
|
||||
m_server_http_code = e.m_http_error_code;
|
||||
m_server_http_string = e.m_http_error_msg;
|
||||
}
|
||||
|
||||
write_handshake();
|
||||
}
|
||||
|
||||
void server_session::write_handshake() {
|
||||
std::stringstream h;
|
||||
|
||||
|
||||
|
||||
if (m_server_http_code == 101) {
|
||||
std::string server_key = get_client_header("Sec-WebSocket-Key");
|
||||
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
SHA1 sha;
|
||||
uint32_t message_digest[5];
|
||||
|
||||
sha.Reset();
|
||||
sha << server_key.c_str();
|
||||
|
||||
if (sha.Result(message_digest)){
|
||||
// convert sha1 hash bytes to network byte order because this sha1
|
||||
// library works on ints rather than bytes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
message_digest[i] = htonl(message_digest[i]);
|
||||
}
|
||||
|
||||
server_key = base64_encode(
|
||||
reinterpret_cast<const unsigned char*>(message_digest),20);
|
||||
|
||||
// set handshake accept headers
|
||||
set_header("Sec-WebSocket-Accept",server_key);
|
||||
set_header("Upgrade","websocket");
|
||||
set_header("Connection","Upgrade");
|
||||
} else {
|
||||
log("Error computing handshake sha1 hash.",LOG_ERROR);
|
||||
m_server_http_code = 500;
|
||||
m_server_http_string = "";
|
||||
}
|
||||
}
|
||||
|
||||
// hardcoded server headers
|
||||
set_header("Server","WebSocket++/2011-09-25");
|
||||
|
||||
h << "HTTP/1.1 " << m_server_http_code << " "
|
||||
<< (m_server_http_string != "" ? m_server_http_string :
|
||||
lookup_http_error_string(m_server_http_code))
|
||||
<< "\r\n";
|
||||
|
||||
header_list::iterator it;
|
||||
for (it = m_server_headers.begin(); it != m_server_headers.end(); it++) {
|
||||
h << it->first << ": " << it->second << "\r\n";
|
||||
}
|
||||
|
||||
h << "\r\n";
|
||||
|
||||
m_raw_server_handshake = h.str();
|
||||
|
||||
// start async write to handle_write_handshake
|
||||
boost::asio::async_write(
|
||||
m_socket,
|
||||
boost::asio::buffer(m_raw_server_handshake),
|
||||
boost::bind(
|
||||
&session::handle_write_handshake,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void server_session::handle_write_handshake(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error writing handshake response",error);
|
||||
return;
|
||||
}
|
||||
|
||||
log_open_result();
|
||||
|
||||
if (m_server_http_code != 101) {
|
||||
std::stringstream err;
|
||||
err << "Handshake ended with HTTP error: " << m_server_http_code << " "
|
||||
<< (m_server_http_string != "" ? m_server_http_string : lookup_http_error_string(m_server_http_code));
|
||||
log(err.str(),LOG_ERROR);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
m_status = OPEN;
|
||||
|
||||
if (m_local_interface) {
|
||||
m_local_interface->on_open(shared_from_this());
|
||||
}
|
||||
|
||||
reset_message();
|
||||
this->read_frame();
|
||||
}
|
||||
|
||||
void server_session::log(const std::string& msg, uint16_t level) const {
|
||||
m_server->log(msg,level);
|
||||
}
|
||||
|
||||
void server_session::access_log(const std::string& msg, uint16_t level) const {
|
||||
m_server->access_log(msg,level);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 WEBSOCKET_SERVER_SESSION_HPP
|
||||
#define WEBSOCKET_SERVER_SESSION_HPP
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace websocketpp {
|
||||
class server_session;
|
||||
typedef boost::shared_ptr<server_session> server_session_ptr;
|
||||
}
|
||||
|
||||
#include "websocket_session.hpp"
|
||||
#include "websocket_server.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
class server_session : public session {
|
||||
public:
|
||||
server_session (server_ptr s,
|
||||
boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc);
|
||||
|
||||
/*** SERVER INTERFACE ***/
|
||||
|
||||
// This function is called when a connection to a new client has been
|
||||
// established and the server is ready to read the client handshake.
|
||||
void on_connect();
|
||||
|
||||
/*** HANDSHAKE INTERFACE ***/
|
||||
|
||||
// Set an HTTP header for the outgoing server handshake response.
|
||||
void set_header(const std::string& key, const std::string& val);
|
||||
|
||||
// Selects a subprotocol for the connection to use. val must be a value
|
||||
// present in the client's opening handshake or the empty string for null.
|
||||
void select_subprotocol(const std::string& val);
|
||||
|
||||
// Selects an extension from the list offered by the client. Each extension
|
||||
// selected must have been offered by the client. Extensions will be used
|
||||
// in the order that they were selected here.
|
||||
void select_extension(const std::string& val);
|
||||
|
||||
/*** SESSION INTERFACE ***/
|
||||
// see session
|
||||
virtual bool is_server() const { return true;}
|
||||
|
||||
void log(const std::string& msg, uint16_t level) const;
|
||||
void access_log(const std::string& msg, uint16_t level) const;
|
||||
protected:
|
||||
// Opening handshake processors and callbacks. These need to be defined in
|
||||
virtual void write_handshake();
|
||||
virtual void handle_write_handshake(const boost::system::error_code& e);
|
||||
virtual void read_handshake();
|
||||
virtual void handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
protected:
|
||||
// connection resources
|
||||
server_ptr m_server;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_SERVER_SESSION_HPP
|
||||
+96
-298
@@ -25,6 +25,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "websocketpp.hpp"
|
||||
#include "websocket_session.hpp"
|
||||
|
||||
#include "websocket_frame.hpp"
|
||||
@@ -42,7 +43,7 @@
|
||||
|
||||
using websocketpp::session;
|
||||
|
||||
session::session (server_ptr s,boost::asio::io_service& io_service,
|
||||
session::session (boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc)
|
||||
: m_status(CONNECTING),
|
||||
m_local_close_code(CLOSE_STATUS_NO_STATUS),
|
||||
@@ -50,8 +51,8 @@ session::session (server_ptr s,boost::asio::io_service& io_service,
|
||||
m_was_clean(false),
|
||||
m_closed_by_me(false),
|
||||
m_dropped_by_me(false),
|
||||
m_server(s),
|
||||
m_socket(io_service),
|
||||
m_io_service(io_service),
|
||||
m_local_interface(defc),
|
||||
|
||||
|
||||
@@ -62,58 +63,65 @@ tcp::socket& session::socket() {
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
void session::start() {
|
||||
// async read to handle_read_handshake
|
||||
boost::asio::async_read_until(
|
||||
m_socket,
|
||||
m_buf,
|
||||
"\r\n\r\n",
|
||||
boost::bind(
|
||||
&session::handle_read_handshake,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);
|
||||
boost::asio::io_service& session::io_service() {
|
||||
return m_io_service;
|
||||
}
|
||||
|
||||
void session::set_handler(connection_handler_ptr new_con) {
|
||||
if (m_local_interface) {
|
||||
m_local_interface->disconnect(shared_from_this(),4000,"Setting new connection handler");
|
||||
// TODO: this should be another method and not reusing onclose
|
||||
//m_local_interface->disconnect(shared_from_this(),4000,"Setting new connection handler");
|
||||
}
|
||||
m_local_interface = new_con;
|
||||
m_local_interface->connect(shared_from_this());
|
||||
m_local_interface->on_open(shared_from_this());
|
||||
}
|
||||
|
||||
std::string session::get_header(const std::string& key) const {
|
||||
std::map<std::string,std::string>::const_iterator h = m_headers.find(key);
|
||||
const std::string& session::get_subprotocol() const {
|
||||
if (m_status == CONNECTING) {
|
||||
log("Subprotocol is not avaliable before the handshake has completed.",LOG_WARN);
|
||||
throw server_error("Subprotocol is not avaliable before the handshake has completed.");
|
||||
}
|
||||
return m_server_subprotocol;
|
||||
}
|
||||
|
||||
const std::string& session::get_resource() const {
|
||||
return m_resource;
|
||||
}
|
||||
|
||||
const std::string& session::get_origin() const {
|
||||
return m_client_origin;
|
||||
}
|
||||
|
||||
std::string session::get_client_header(const std::string& key) const {
|
||||
return get_header(key,m_client_headers);
|
||||
}
|
||||
|
||||
std::string session::get_server_header(const std::string& key) const {
|
||||
return get_header(key,m_server_headers);
|
||||
}
|
||||
|
||||
std::string session::get_header(const std::string& key,
|
||||
const header_list& list) const {
|
||||
header_list::const_iterator h = list.find(key);
|
||||
|
||||
if (h == m_headers.end()) {
|
||||
return std::string();
|
||||
if (h == list.end()) {
|
||||
return "";
|
||||
} else {
|
||||
return h->second;
|
||||
}
|
||||
}
|
||||
|
||||
void session::add_header(const std::string &key,const std::string &value) {
|
||||
throw "unimplimented";
|
||||
const std::vector<std::string>& session::get_extensions() const {
|
||||
return m_server_extensions;
|
||||
}
|
||||
|
||||
std::string session::get_request() const {
|
||||
return m_request;
|
||||
}
|
||||
|
||||
std::string session::get_origin() const {
|
||||
if (m_version < 13) {
|
||||
return get_header("Sec-WebSocket-Origin");
|
||||
} else {
|
||||
return get_header("Origin");
|
||||
}
|
||||
unsigned int session::get_version() const {
|
||||
return m_version;
|
||||
}
|
||||
|
||||
void session::send(const std::string &msg) {
|
||||
if (m_status != OPEN) {
|
||||
// error?
|
||||
log("Tried to send a message from a session that wasn't open",LOG_WARN);
|
||||
return;
|
||||
}
|
||||
m_write_frame.set_fin(true);
|
||||
@@ -123,10 +131,9 @@ void session::send(const std::string &msg) {
|
||||
write_frame();
|
||||
}
|
||||
|
||||
// send binary frame
|
||||
void session::send(const std::vector<unsigned char> &data) {
|
||||
if (m_status != OPEN) {
|
||||
// error?
|
||||
log("Tried to send a message from a session that wasn't open",LOG_WARN);
|
||||
return;
|
||||
}
|
||||
m_write_frame.set_fin(true);
|
||||
@@ -136,10 +143,15 @@ void session::send(const std::vector<unsigned char> &data) {
|
||||
write_frame();
|
||||
}
|
||||
|
||||
// send close frame
|
||||
void session::close(uint16_t status,const std::string& msg) {
|
||||
disconnect(status,msg);
|
||||
// TODO: close behavior
|
||||
}
|
||||
|
||||
// TODO: clean this up, needs to be broken out into more specific methods
|
||||
void session::disconnect(uint16_t status,const std::string &message) {
|
||||
if (m_status != OPEN) {
|
||||
m_server->error_log("got a disconnect call from invalid state");
|
||||
log("Tried to disconnect a session that wasn't open",LOG_WARN);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -158,6 +170,7 @@ void session::disconnect(uint16_t status,const std::string &message) {
|
||||
m_write_frame.set_status(CLOSE_STATUS_NORMAL,"");
|
||||
} else if (status == CLOSE_STATUS_ABNORMAL_CLOSE) {
|
||||
// unknown internal error, don't set a status? use protocol error?
|
||||
log("Tried to disconnect with status ABNORMAL_CLOSE",LOG_DEBUG);
|
||||
} else {
|
||||
m_write_frame.set_status(status,message);
|
||||
}
|
||||
@@ -166,6 +179,10 @@ void session::disconnect(uint16_t status,const std::string &message) {
|
||||
}
|
||||
|
||||
void session::ping(const std::string &msg) {
|
||||
if (m_status != OPEN) {
|
||||
log("Tried to send a ping from a session that wasn't open",LOG_WARN);
|
||||
return;
|
||||
}
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_opcode(frame::PING);
|
||||
m_write_frame.set_payload(msg);
|
||||
@@ -174,6 +191,10 @@ void session::ping(const std::string &msg) {
|
||||
}
|
||||
|
||||
void session::pong(const std::string &msg) {
|
||||
if (m_status != OPEN) {
|
||||
log("Tried to send a pong from a session that wasn't open",LOG_WARN);
|
||||
return;
|
||||
}
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_opcode(frame::PONG);
|
||||
m_write_frame.set_payload(msg);
|
||||
@@ -181,230 +202,6 @@ void session::pong(const std::string &msg) {
|
||||
write_frame();
|
||||
}
|
||||
|
||||
void session::handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred) {
|
||||
// read handshake and set local state (or pass to write_handshake)
|
||||
std::ostringstream line;
|
||||
line << &m_buf;
|
||||
m_handshake += line.str();
|
||||
|
||||
//std::cout << "=== Raw Message ===" << std::endl;
|
||||
//std::cout << m_handshake << std::endl;
|
||||
//std::cout << "=== Raw Message end ===" << std::endl;
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type end;
|
||||
|
||||
// Get request and parse headers
|
||||
end = m_handshake.find("\r\n",start);
|
||||
|
||||
while(end != std::string::npos) {
|
||||
tokens.push_back(m_handshake.substr(start, end - start));
|
||||
|
||||
start = end + 2;
|
||||
|
||||
end = m_handshake.find("\r\n",start);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tokens.size(); i++) {
|
||||
if (i == 0) {
|
||||
m_request = tokens[i];
|
||||
}
|
||||
|
||||
end = tokens[i].find(": ",0);
|
||||
|
||||
if (end != std::string::npos) {
|
||||
m_headers[tokens[i].substr(0,end)] = tokens[i].substr(end+2);
|
||||
}
|
||||
}
|
||||
|
||||
// handshake error checking
|
||||
try {
|
||||
std::stringstream err;
|
||||
std::string h;
|
||||
|
||||
// check the method
|
||||
if (m_request.substr(0,4) != "GET ") {
|
||||
err << "Websocket handshake has invalid method: "
|
||||
<< m_request.substr(0,4);
|
||||
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
// check the HTTP version
|
||||
// TODO: allow versions greater than 1.1
|
||||
end = m_request.find(" HTTP/1.1",4);
|
||||
if (end == std::string::npos) {
|
||||
err << "Websocket handshake has invalid HTTP version";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
m_request = m_request.substr(4,end-4);
|
||||
|
||||
// verify the presence of required headers
|
||||
h = get_header("Host");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Host header is missing",400));
|
||||
} else if (!m_server->validate_host(h)) {
|
||||
err << "Host " << h << " is not one of this server's names.";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
h = get_header("Upgrade");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Upgrade header is missing",400));
|
||||
} else if (!boost::iequals(h,"websocket")) {
|
||||
err << "Upgrade header was " << h << " instead of \"websocket\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
h = get_header("Connection");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Connection header is missing",400));
|
||||
} else if (!boost::ifind_first(h,"upgrade")) {
|
||||
err << "Connection header, \"" << h
|
||||
<< "\", does not contain required token \"upgrade\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
if (get_header("Sec-WebSocket-Key") == "") {
|
||||
throw(handshake_error("Required Sec-WebSocket-Key header is missing",400));
|
||||
}
|
||||
|
||||
h = get_header("Sec-WebSocket-Version");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Sec-WebSocket-Version header is missing",400));
|
||||
} else {
|
||||
m_version = atoi(h.c_str());
|
||||
|
||||
if (m_version != 7 && m_version != 8 && m_version != 13) {
|
||||
err << "This server doesn't support WebSocket protocol version "
|
||||
<< m_version;
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
}
|
||||
|
||||
// optional headers (delegated to the local interface)
|
||||
if (m_local_interface) {
|
||||
m_local_interface->validate(shared_from_this());
|
||||
}
|
||||
|
||||
} catch (const handshake_error& e) {
|
||||
std::stringstream err;
|
||||
err << "Caught handshake exception: " << e.what();
|
||||
|
||||
m_server->error_log(err.str());
|
||||
e.write(shared_from_this());
|
||||
return;
|
||||
}
|
||||
|
||||
this->write_handshake();
|
||||
}
|
||||
|
||||
void session::write_handshake() {
|
||||
std::string server_handshake = "";
|
||||
std::string server_key = m_headers["Sec-WebSocket-Key"];
|
||||
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
SHA1 sha;
|
||||
uint32_t message_digest[5];
|
||||
|
||||
sha.Reset();
|
||||
sha << server_key.c_str();
|
||||
|
||||
if (!sha.Result(message_digest)) {
|
||||
m_server->error_log("Error computing handshake sha1 hash.");
|
||||
write_http_error(500,"");
|
||||
return;
|
||||
}
|
||||
|
||||
// convert sha1 hash bytes to network byte order because this sha1
|
||||
// library works on ints rather than bytes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
message_digest[i] = htonl(message_digest[i]);
|
||||
}
|
||||
|
||||
server_key = base64_encode(
|
||||
reinterpret_cast<const unsigned char*>(message_digest),20);
|
||||
|
||||
server_handshake += "HTTP/1.1 101 Switching Protocols\r\n";
|
||||
server_handshake += "Upgrade: websocket\r\n";
|
||||
server_handshake += "Connection: Upgrade\r\n";
|
||||
server_handshake += "Sec-WebSocket-Accept: "+server_key+"\r\n";
|
||||
server_handshake += "Server: WebSocket++/2011-09-22\r\n\r\n";
|
||||
|
||||
// TODO: handler requested headers
|
||||
|
||||
// start async write to handle_write_handshake
|
||||
boost::asio::async_write(
|
||||
m_socket,
|
||||
boost::asio::buffer(server_handshake),
|
||||
boost::bind(
|
||||
&session::handle_write_handshake,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void session::handle_write_handshake(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error writing handshake",error);
|
||||
return;
|
||||
}
|
||||
|
||||
access_log_open(101);
|
||||
|
||||
m_status = OPEN;
|
||||
|
||||
if (m_local_interface) {
|
||||
m_local_interface->connect(shared_from_this());
|
||||
}
|
||||
|
||||
reset_message();
|
||||
this->read_frame();
|
||||
}
|
||||
|
||||
void session::write_http_error(int code,const std::string &msg) {
|
||||
std::stringstream server_handshake;
|
||||
|
||||
server_handshake << "HTTP/1.1 " << code << " "
|
||||
<< (msg != "" ? msg : lookup_http_error_string(code))
|
||||
<< "\r\n"
|
||||
<< "Server: WebSocket++/2011-09-22\r\n";
|
||||
|
||||
// additional headers?
|
||||
|
||||
server_handshake << "\r\n";
|
||||
|
||||
// start async write to handle_write_handshake
|
||||
boost::asio::async_write(
|
||||
m_socket,
|
||||
boost::asio::buffer(server_handshake.str()),
|
||||
boost::bind(
|
||||
&session::handle_write_http_error,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
|
||||
access_log_open(code);
|
||||
|
||||
std::stringstream err;
|
||||
err << "Handshake ended with HTTP error: " << code << " "
|
||||
<< (msg != "" ? msg : lookup_http_error_string(code));
|
||||
|
||||
m_server->error_log(err.str());
|
||||
}
|
||||
|
||||
void session::handle_write_http_error(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error writing http response",error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void session::read_frame() {
|
||||
boost::asio::async_read(
|
||||
m_socket,
|
||||
@@ -418,11 +215,10 @@ void session::read_frame() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void session::handle_frame_header(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error reading basic frame header",error);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -431,6 +227,7 @@ void session::handle_frame_header(const boost::system::error_code& error) {
|
||||
if (!m_read_frame.validate_basic_header()) {
|
||||
handle_error("Basic header validation failed",boost::system::error_code());
|
||||
disconnect(CLOSE_STATUS_PROTOCOL_ERROR,"");
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -454,6 +251,7 @@ void session::handle_extended_frame_header(
|
||||
const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error reading extended frame header",error);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -464,16 +262,6 @@ void session::handle_extended_frame_header(
|
||||
}
|
||||
|
||||
void session::read_payload() {
|
||||
/*char * foo = m_read_frame.get_header();
|
||||
|
||||
std::cout << std::hex << ((uint16_t*)foo)[0] << std::endl;
|
||||
|
||||
std::cout << "opcode: " << m_read_frame.get_opcode() << std::endl;
|
||||
std::cout << "fin: " << m_read_frame.get_fin() << std::endl;
|
||||
std::cout << "mask: " << m_read_frame.get_masked() << std::endl;
|
||||
std::cout << "size: " << (uint16_t)m_read_frame.get_basic_size() << std::endl;
|
||||
std::cout << "payload_size: " << m_read_frame.get_payload_size() << std::endl;*/
|
||||
|
||||
boost::asio::async_read(
|
||||
m_socket,
|
||||
boost::asio::buffer(m_read_frame.get_payload()),
|
||||
@@ -488,11 +276,11 @@ void session::read_payload() {
|
||||
void session::handle_read_payload (const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error reading payload data frame header",error);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
m_read_frame.process_payload();
|
||||
|
||||
|
||||
if (m_status == OPEN) {
|
||||
switch (m_read_frame.get_opcode()) {
|
||||
@@ -516,6 +304,7 @@ void session::handle_read_payload (const boost::system::error_code& error) {
|
||||
break;
|
||||
default:
|
||||
disconnect(CLOSE_STATUS_PROTOCOL_ERROR,"Invalid Opcode");
|
||||
// TODO: close behavior
|
||||
break;
|
||||
}
|
||||
} else if (m_status == CLOSING) {
|
||||
@@ -526,23 +315,26 @@ void session::handle_read_payload (const boost::system::error_code& error) {
|
||||
}
|
||||
} else {
|
||||
// Recieved message before or after connection was opened/closed
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
// check if there was an error processing this frame and fail the connection
|
||||
if (m_error) {
|
||||
m_server->error_log("Connection has been closed uncleanly");
|
||||
log("Connection has been closed uncleanly",LOG_ERROR);
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_status == CLOSED) {
|
||||
access_log_close();
|
||||
log_close_result();
|
||||
|
||||
if (m_local_interface) {
|
||||
m_local_interface->disconnect(shared_from_this(),
|
||||
m_close_code,
|
||||
m_close_message);
|
||||
m_local_interface->on_close(shared_from_this(),
|
||||
m_close_code,
|
||||
m_close_message);
|
||||
}
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -552,14 +344,16 @@ void session::handle_read_payload (const boost::system::error_code& error) {
|
||||
void session::handle_write_frame (const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
handle_error("Error writing frame data",error);
|
||||
// TODO: close behavior
|
||||
}
|
||||
|
||||
//std::cout << "Successfully wrote frame." << std::endl;
|
||||
}
|
||||
|
||||
void session::process_ping() {
|
||||
m_server->access_log("Ping");
|
||||
|
||||
access_log("Ping",ALOG_MISC_CONTROL);
|
||||
// TODO: on_ping
|
||||
|
||||
// send pong
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_opcode(frame::PONG);
|
||||
@@ -569,12 +363,14 @@ void session::process_ping() {
|
||||
}
|
||||
|
||||
void session::process_pong() {
|
||||
m_server->access_log("Pong");
|
||||
access_log("Pong",ALOG_MISC_CONTROL);
|
||||
// TODO: on_pong
|
||||
}
|
||||
|
||||
void session::process_text() {
|
||||
if (!m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint)) {
|
||||
disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data");
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -586,6 +382,7 @@ void session::process_binary() {
|
||||
handle_error("Got a new message before the previous was finished.",
|
||||
boost::system::error_code());
|
||||
disconnect(CLOSE_STATUS_PROTOCOL_ERROR,"");
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -605,12 +402,14 @@ void session::process_continuation() {
|
||||
handle_error("Got a continuation frame without an outstanding message.",
|
||||
boost::system::error_code());
|
||||
disconnect(CLOSE_STATUS_PROTOCOL_ERROR,"");
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_current_opcode == frame::TEXT_FRAME) {
|
||||
if (!m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint)) {
|
||||
disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data");
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -635,6 +434,7 @@ void session::process_close() {
|
||||
if (m_status == OPEN) {
|
||||
// This is the case where the remote initiated the close.
|
||||
m_closed_by_me = false;
|
||||
// TODO: close behavior
|
||||
disconnect(status,message);
|
||||
} else if (m_status == CLOSING) {
|
||||
// this is an ack of our close message
|
||||
@@ -654,9 +454,9 @@ void session::deliver_message() {
|
||||
|
||||
if (m_current_opcode == frame::BINARY_FRAME) {
|
||||
if (m_fragmented) {
|
||||
m_local_interface->message(shared_from_this(),m_current_message);
|
||||
m_local_interface->on_message(shared_from_this(),m_current_message);
|
||||
} else {
|
||||
m_local_interface->message(shared_from_this(),
|
||||
m_local_interface->on_message(shared_from_this(),
|
||||
m_read_frame.get_payload());
|
||||
}
|
||||
} else if (m_current_opcode == frame::TEXT_FRAME) {
|
||||
@@ -665,6 +465,7 @@ void session::deliver_message() {
|
||||
// make sure the finished frame is valid utf8
|
||||
if (m_utf8_state != utf8_validator::UTF8_ACCEPT) {
|
||||
disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data");
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -677,12 +478,12 @@ void session::deliver_message() {
|
||||
);
|
||||
}
|
||||
|
||||
m_local_interface->message(shared_from_this(),msg);
|
||||
m_local_interface->on_message(shared_from_this(),msg);
|
||||
} else {
|
||||
// Not sure if this should be a fatal error or not
|
||||
std::stringstream err;
|
||||
err << "Attempted to deliver a message of unsupported opcode " << m_current_opcode;
|
||||
m_server->error_log(err.str());
|
||||
log(err.str(),LOG_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -694,9 +495,6 @@ void session::extract_payload() {
|
||||
}
|
||||
|
||||
void session::write_frame() {
|
||||
// print debug info
|
||||
m_write_frame.print_frame();
|
||||
|
||||
std::vector<boost::asio::mutable_buffer> data;
|
||||
|
||||
data.push_back(
|
||||
@@ -729,7 +527,7 @@ void session::reset_message() {
|
||||
m_utf8_codepoint = 0;
|
||||
}
|
||||
|
||||
void session::access_log_close() {
|
||||
void session::log_close_result() {
|
||||
std::stringstream msg;
|
||||
|
||||
msg << "[Connection " << this << "] "
|
||||
@@ -739,19 +537,19 @@ void session::access_log_close() {
|
||||
<< "] remote:[" << m_remote_close_code
|
||||
<< (m_remote_close_msg == "" ? "" : ","+m_remote_close_msg) << "]";
|
||||
|
||||
m_server->access_log(msg.str());
|
||||
access_log(msg.str(),ALOG_DISCONNECT);
|
||||
}
|
||||
|
||||
void session::access_log_open(int code) {
|
||||
void session::log_open_result() {
|
||||
std::stringstream msg;
|
||||
|
||||
msg << "[Connection " << this << "] "
|
||||
<< m_socket.remote_endpoint()
|
||||
<< " v" << m_version << " "
|
||||
<< (get_header("User-Agent") == "" ? "NULL" : get_header("User-Agent"))
|
||||
<< " " << m_request << " " << code;
|
||||
<< (get_client_header("User-Agent") == "" ? "NULL" : get_client_header("User-Agent"))
|
||||
<< " " << m_resource << " " << m_server_http_code;
|
||||
|
||||
m_server->access_log(msg.str());
|
||||
access_log(msg.str(),ALOG_HANDSHAKE);
|
||||
}
|
||||
|
||||
void session::handle_error(std::string msg,
|
||||
@@ -760,10 +558,10 @@ void session::handle_error(std::string msg,
|
||||
|
||||
e << "[Connection " << this << "] " << msg << " (" << error << ")";
|
||||
|
||||
m_server->error_log(e.str());
|
||||
log(e.str(),LOG_ERROR);
|
||||
|
||||
if (m_local_interface) {
|
||||
m_local_interface->disconnect(shared_from_this(),1006,e.str());
|
||||
m_local_interface->on_close(shared_from_this(),1006,e.str());
|
||||
}
|
||||
|
||||
m_error = true;
|
||||
|
||||
+66
-73
@@ -52,7 +52,7 @@ namespace websocketpp {
|
||||
class handshake_error;
|
||||
}
|
||||
|
||||
#include "websocket_server.hpp"
|
||||
#include "websocketpp.hpp"
|
||||
#include "websocket_frame.hpp"
|
||||
#include "websocket_connection_handler.hpp"
|
||||
|
||||
@@ -63,6 +63,8 @@ using boost::asio::ip::tcp;
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
typedef std::map<std::string,std::string> header_list;
|
||||
|
||||
class session : public boost::enable_shared_from_this<session> {
|
||||
public:
|
||||
friend class handshake_error;
|
||||
@@ -86,12 +88,12 @@ public:
|
||||
static const uint16_t CLOSE_STATUS_POLICY_VIOLATION = 1008;
|
||||
static const uint16_t CLOSE_STATUS_MESSAGE_TOO_BIG = 1009;
|
||||
static const uint16_t CLOSE_STATUS_EXTENSION_REQUIRE = 1010;
|
||||
|
||||
session (server_ptr s,
|
||||
boost::asio::io_service& io_service,
|
||||
|
||||
session (boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc);
|
||||
|
||||
tcp::socket& socket();
|
||||
boost::asio::io_service& io_service();
|
||||
|
||||
/*** SERVER INTERFACE ***/
|
||||
|
||||
@@ -99,7 +101,7 @@ public:
|
||||
// that come after it are called as a result of an async event completing.
|
||||
// if any method in this chain returns before adding a new async event the
|
||||
// session will end.
|
||||
void start();
|
||||
virtual void on_connect() = 0;
|
||||
|
||||
// sets the internal connection handler of this connection to new_con.
|
||||
// This is useful if you want to switch handler objects during a connection
|
||||
@@ -110,24 +112,20 @@ public:
|
||||
|
||||
|
||||
/*** HANDSHAKE INTERFACE ***/
|
||||
// Set session connection information (avaliable only before/during the
|
||||
// opening handshake)
|
||||
|
||||
// gets the value of a header or the empty string if not present.
|
||||
std::string get_header(const std::string &key) const;
|
||||
// adds an arbitrary header to the server handshake HTTP response.
|
||||
void add_header(const std::string &key,const std::string &value);
|
||||
// Get session status (valid once the connection is open)
|
||||
|
||||
std::string get_request() const;
|
||||
std::string get_origin() const;
|
||||
|
||||
// sets the subprotocol being used. This will result in the appropriate
|
||||
// Sec-WebSocket-Protocol header being sent back to the client. The value
|
||||
// here must have been present in the client's opening handshake.
|
||||
void set_subprotocol(const std::string &protocol);
|
||||
|
||||
|
||||
//int get_version();
|
||||
|
||||
//void add_extension();
|
||||
// returns the subprotocol that was negotiated during the opening handshake
|
||||
// or the empty string if no subprotocol was requested.
|
||||
const std::string& get_subprotocol() const;
|
||||
const std::string& get_resource() const;
|
||||
const std::string& get_origin() const;
|
||||
std::string get_client_header(const std::string& key) const;
|
||||
std::string get_server_header(const std::string& key) const;
|
||||
const std::vector<std::string>& get_extensions() const;
|
||||
unsigned int get_version() const;
|
||||
|
||||
/*** SESSION INTERFACE ***/
|
||||
|
||||
@@ -137,45 +135,29 @@ public:
|
||||
void ping(const std::string &msg);
|
||||
void pong(const std::string &msg);
|
||||
|
||||
void disconnect(uint16_t status,const std::string &reason);
|
||||
private:
|
||||
// handle_read_handshake reads the HTTP headers of the initial websocket
|
||||
// handshake, parses out the request and headers, and does error checking
|
||||
// TODO: Generalize a lot of the hard coded things in this method.
|
||||
void handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
// write_handshake calculates the server portion of the handshake and
|
||||
// sends it back.
|
||||
// TODO: Generalize this to include things like protocols, cookies, etc
|
||||
void write_handshake();
|
||||
// handle_write_handshake checks for errors writing the server handshake,
|
||||
// officially declares a connection open, notifies the local interface,
|
||||
// and starts the frame reading loop.
|
||||
void handle_write_handshake(const boost::system::error_code& error);
|
||||
|
||||
// construct and write an HTTP error in the case the handshake goes poorly
|
||||
void write_http_error(int http_code,const std::string &http_err_str);
|
||||
void handle_write_http_error(const boost::system::error_code& error);
|
||||
// initiate a connection close
|
||||
void close(uint16_t status,const std::string &reason);
|
||||
void disconnect(uint16_t status,const std::string& reason); // temp
|
||||
|
||||
virtual bool is_server() const = 0;
|
||||
protected:
|
||||
// Opening handshake processors and callbacks. These need to be defined in
|
||||
// derived classes.
|
||||
virtual void write_handshake() = 0;
|
||||
virtual void handle_write_handshake(const boost::system::error_code& e) = 0;
|
||||
virtual void read_handshake() = 0;
|
||||
virtual void handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred) = 0;
|
||||
|
||||
// start async read for a websocket frame (2 bytes) to handle_frame_header
|
||||
void read_frame();
|
||||
|
||||
// reads frame header and devices if it needs to read more header or go
|
||||
// straight to the payload.
|
||||
void handle_frame_header(const boost::system::error_code& error);
|
||||
|
||||
// process extra headers and start payload read
|
||||
void handle_extended_frame_header(const boost::system::error_code& error);
|
||||
|
||||
// initiate payload read
|
||||
void read_payload();
|
||||
|
||||
// now the frame object should be complete. Process and send it on then
|
||||
// reset for new frame
|
||||
void handle_read_payload (const boost::system::error_code& error);
|
||||
|
||||
// checks for errors writing frames
|
||||
// write m_write_frame out to the socket.
|
||||
void write_frame();
|
||||
void handle_write_frame (const boost::system::error_code& error);
|
||||
|
||||
// helper functions for processing each opcode
|
||||
@@ -194,27 +176,43 @@ private:
|
||||
// messages are recieved.
|
||||
void extract_payload();
|
||||
|
||||
// write m_write_frame out to the socket.
|
||||
void write_frame();
|
||||
|
||||
// reset session for a new message
|
||||
void reset_message();
|
||||
|
||||
// prints connection state to the server access log with given http response
|
||||
// code
|
||||
void access_log_close();
|
||||
void access_log_open(int code);
|
||||
// logging
|
||||
virtual void log(const std::string& msg, uint16_t level) const = 0;
|
||||
virtual void access_log(const std::string& msg, uint16_t level) const = 0;
|
||||
|
||||
void log_close_result();
|
||||
void log_open_result();
|
||||
|
||||
// prints a diagnostic message and disconnects the local interface
|
||||
void handle_error(std::string msg,const boost::system::error_code& error);
|
||||
private:
|
||||
std::string get_header(const std::string& key,
|
||||
const header_list& list) const;
|
||||
|
||||
protected:
|
||||
// Immutable state about the current connection from the handshake
|
||||
std::string m_handshake;
|
||||
std::string m_request;
|
||||
std::map<std::string,std::string> m_headers;
|
||||
unsigned int m_version;
|
||||
std::string m_subprotocol;
|
||||
|
||||
// Client handshake
|
||||
std::string m_raw_client_handshake;
|
||||
std::string m_client_http_request;
|
||||
std::string m_resource;
|
||||
std::string m_client_origin;
|
||||
header_list m_client_headers;
|
||||
std::vector<std::string> m_client_subprotocols;
|
||||
std::vector<std::string> m_client_extensions;
|
||||
unsigned int m_version;
|
||||
|
||||
// Server handshake
|
||||
std::string m_raw_server_handshake;
|
||||
std::string m_server_http_request;
|
||||
header_list m_server_headers;
|
||||
std::string m_server_subprotocol;
|
||||
std::vector<std::string> m_server_extensions;
|
||||
uint16_t m_server_http_code;
|
||||
std::string m_server_http_string;
|
||||
|
||||
// Mutable connection state;
|
||||
status_code m_status;
|
||||
uint16_t m_close_code;
|
||||
@@ -230,9 +228,9 @@ private:
|
||||
bool m_dropped_by_me;
|
||||
|
||||
// Connection Resources
|
||||
server_ptr m_server;
|
||||
tcp::socket m_socket;
|
||||
connection_handler_ptr m_local_interface;
|
||||
tcp::socket m_socket;
|
||||
boost::asio::io_service& m_io_service;
|
||||
connection_handler_ptr m_local_interface;
|
||||
|
||||
// Buffers
|
||||
boost::asio::streambuf m_buf;
|
||||
@@ -266,11 +264,6 @@ public:
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
void write(session_ptr s) const {
|
||||
s->write_http_error(m_http_error_code,m_http_error_msg);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_msg;
|
||||
int m_http_error_code;
|
||||
std::string m_http_error_msg;
|
||||
|
||||
@@ -30,11 +30,42 @@
|
||||
#ifndef WEBSOCKETPP_HPP
|
||||
#define WEBSOCKETPP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Defaults
|
||||
namespace websocketpp {
|
||||
const uint64_t DEFAULT_MAX_MESSAGE_SIZE = 0xFFFFFF; // ~16MB
|
||||
|
||||
// System logging levels
|
||||
static const uint16_t LOG_ALL = 0;
|
||||
static const uint16_t LOG_DEBUG = 1;
|
||||
static const uint16_t LOG_INFO = 2;
|
||||
static const uint16_t LOG_WARN = 3;
|
||||
static const uint16_t LOG_ERROR = 4;
|
||||
static const uint16_t LOG_FATAL = 5;
|
||||
static const uint16_t LOG_OFF = 6;
|
||||
|
||||
// Access logging controls
|
||||
// Individual bits
|
||||
static const uint16_t ALOG_CONNECT = 0x1;
|
||||
static const uint16_t ALOG_DISCONNECT = 0x2;
|
||||
static const uint16_t ALOG_MISC_CONTROL = 0x4;
|
||||
static const uint16_t ALOG_FRAME = 0x8;
|
||||
static const uint16_t ALOG_MESSAGE = 0x10;
|
||||
static const uint16_t ALOG_INFO = 0x20;
|
||||
static const uint16_t ALOG_HANDSHAKE = 0x40;
|
||||
// Useful groups
|
||||
static const uint16_t ALOG_OFF = 0x0;
|
||||
static const uint16_t ALOG_CONTROL = ALOG_CONNECT
|
||||
& ALOG_DISCONNECT
|
||||
& ALOG_MISC_CONTROL;
|
||||
static const uint16_t ALOG_ALL = 0xFFFF;
|
||||
}
|
||||
|
||||
#include "websocket_session.hpp"
|
||||
#include "websocket_server_session.hpp"
|
||||
#include "websocket_client_session.hpp"
|
||||
#include "websocket_server.hpp"
|
||||
#include "websocket_client.hpp"
|
||||
|
||||
#endif // WEBSOCKETPP_HPP
|
||||
Reference in new issue
Block a user