Completed new sessions system

Co-authored-by: Thulinma <jaron@vietors.com>
This commit is contained in:
Marco van Dijk 2022-03-16 13:46:14 +01:00 committed by Thulinma
parent 074e757028
commit 8ac486b815
36 changed files with 991 additions and 620 deletions

View file

@ -3,6 +3,7 @@
#include "comms.h"
#include "defines.h"
#include "encode.h"
#include "stream.h"
#include "procs.h"
#include "timing.h"
#include <fcntl.h>
@ -10,6 +11,34 @@
#include "config.h"
namespace Comms{
uint8_t sessionViewerMode = SESS_BUNDLE_DEFAULT_VIEWER;
uint8_t sessionInputMode = SESS_BUNDLE_DEFAULT_OTHER;
uint8_t sessionOutputMode = SESS_BUNDLE_DEFAULT_OTHER;
uint8_t sessionUnspecifiedMode = 0;
uint8_t sessionStreamInfoMode = SESS_DEFAULT_STREAM_INFO_MODE;
uint8_t tknMode = SESS_TKN_DEFAULT_MODE;
/// \brief Refreshes the session configuration if the last update was more than 5 seconds ago
void sessionConfigCache(){
static uint64_t lastUpdate = 0;
if (Util::bootSecs() > lastUpdate + 5){
VERYHIGH_MSG("Updating session config");
JSON::Value tmpVal = Util::getGlobalConfig("sessionViewerMode");
if (!tmpVal.isNull()){ sessionViewerMode = tmpVal.asInt(); }
tmpVal = Util::getGlobalConfig("sessionInputMode");
if (!tmpVal.isNull()){ sessionInputMode = tmpVal.asInt(); }
tmpVal = Util::getGlobalConfig("sessionOutputMode");
if (!tmpVal.isNull()){ sessionOutputMode = tmpVal.asInt(); }
tmpVal = Util::getGlobalConfig("sessionUnspecifiedMode");
if (!tmpVal.isNull()){ sessionUnspecifiedMode = tmpVal.asInt(); }
tmpVal = Util::getGlobalConfig("sessionStreamInfoMode");
if (!tmpVal.isNull()){ sessionStreamInfoMode = tmpVal.asInt(); }
tmpVal = Util::getGlobalConfig("tknMode");
if (!tmpVal.isNull()){ tknMode = tmpVal.asInt(); }
lastUpdate = Util::bootSecs();
}
}
Comms::Comms(){
index = INVALID_RECORD_INDEX;
currentSize = 0;
@ -17,7 +46,7 @@ namespace Comms{
}
Comms::~Comms(){
if (index != INVALID_RECORD_INDEX){
if (index != INVALID_RECORD_INDEX && status){
setStatus(COMM_STATUS_DISCONNECT | getStatus());
}
if (master){
@ -123,6 +152,10 @@ namespace Comms{
return;
}
dataAccX = Util::RelAccX(dataPage.mapped);
if (dataAccX.isExit()){
dataPage.close();
return;
}
fieldAccess();
if (index == INVALID_RECORD_INDEX || reIssue){
size_t reqCount = dataAccX.getRCount();
@ -170,19 +203,30 @@ namespace Comms{
void Sessions::addFields(){
Connections::addFields();
dataAccX.addField("tags", RAX_STRING, 512);
dataAccX.addField("sessid", RAX_STRING, 80);
}
void Sessions::nullFields(){
Connections::nullFields();
setSessId("");
setTags("");
}
void Sessions::fieldAccess(){
Connections::fieldAccess();
tags = dataAccX.getFieldAccX("tags");
sessId = dataAccX.getFieldAccX("sessid");
}
std::string Sessions::getTags() const{return tags.string(index);}
std::string Sessions::getTags(size_t idx) const{return (master ? tags.string(idx) : 0);}
void Sessions::setTags(std::string _sid){tags.set(_sid, index);}
void Sessions::setTags(std::string _sid, size_t idx){
if (!master){return;}
tags.set(_sid, idx);
}
Users::Users() : Comms(){}
Users::Users(const Users &rhs) : Comms(){
@ -251,31 +295,60 @@ namespace Comms{
keyNum.set(_keyNum, idx);
}
void Connections::reload(const std::string & sessId, bool _master, bool reIssue){
// Open SEM_SESSION
if(!sem){
char semName[NAME_BUFFER_SIZE];
snprintf(semName, NAME_BUFFER_SIZE, SEM_SESSION, sessId.c_str());
sem.open(semName, O_RDWR, ACCESSPERMS, 1);
if (!sem){return;}
}
char userPageName[NAME_BUFFER_SIZE];
snprintf(userPageName, NAME_BUFFER_SIZE, COMMS_SESSIONS, sessId.c_str());
Comms::reload(userPageName, COMMS_SESSIONS_INITSIZE, _master, reIssue);
}
/// \brief Claims a spot on the connections page for the input/output which calls this function
/// Starts the MistSession binary for each session, which handles the statistics
/// and the USER_NEW and USER_END triggers
/// \param streamName: Name of the stream the input is providing or an output is making available to viewers
/// \param ip: IP address of the viewer which wants to access streamName. For inputs this value can be set to any value
/// \param sid: Session ID given by the player or randomly generated
/// \param tkn: Session token given by the player or randomly generated
/// \param protocol: Protocol currently in use for this connection
/// \param sessionMode: Determines how a viewer session is defined:
// If set to 0, all connections with the same viewer IP and stream name are bundled.
// If set to 1, all connections with the same viewer IP and player ID are bundled.
// If set to 2, all connections with the same player ID and stream name are bundled.
// If set to 3, all connections with the same viewer IP, player ID and stream name are bundled.
/// \param _master: If True, we are reading from this page. If False, we are writing (to our entry) on this page
/// \param reIssue: If True, claim a new entry on this page
void Connections::reload(std::string streamName, std::string ip, std::string sid, std::string protocol, std::string reqUrl, uint64_t sessionMode, bool _master, bool reIssue){
if (sessionMode == 0xFFFFFFFFFFFFFFFFull){
FAIL_MSG("The session mode was not initialised properly. Assuming default behaviour of bundling by viewer IP, stream name and player id");
sessionMode = SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID;
}
void Connections::reload(const std::string & streamName, const std::string & ip, const std::string & tkn, const std::string & protocol, const std::string & reqUrl, bool _master, bool reIssue){
initialTkn = tkn;
uint8_t sessMode = sessionViewerMode;
// Generate a unique session ID for each viewer, input or output
sessionId = generateSession(streamName, ip, sid, protocol, sessionMode);
if (protocol.size() >= 6 && protocol.substr(0, 6) == "INPUT:"){
sessionId = "I" + sessionId;
sessMode = sessionInputMode;
sessionId = "I" + generateSession(streamName, ip, tkn, protocol, sessMode);
}else if (protocol.size() >= 7 && protocol.substr(0, 7) == "OUTPUT:"){
sessionId = "O" + sessionId;
sessMode = sessionOutputMode;
sessionId = "O" + generateSession(streamName, ip, tkn, protocol, sessMode);
}else{
// If the session only contains the HTTP connector, check sessionStreamInfoMode
if (protocol.size() == 4 && protocol == "HTTP"){
if (sessionStreamInfoMode == SESS_HTTP_AS_VIEWER){
sessionId = generateSession(streamName, ip, tkn, protocol, sessMode);
}else if (sessionStreamInfoMode == SESS_HTTP_AS_OUTPUT){
sessMode = sessionOutputMode;
sessionId = "O" + generateSession(streamName, ip, tkn, protocol, sessMode);
}else if (sessionStreamInfoMode == SESS_HTTP_DISABLED){
return;
}else if (sessionStreamInfoMode == SESS_HTTP_AS_UNSPECIFIED){
// Set sessMode to include all variables when determining the session ID
sessMode = sessionUnspecifiedMode;
sessionId = "U" + generateSession(streamName, ip, tkn, protocol, sessMode);
}else{
sessionId = generateSession(streamName, ip, tkn, protocol, sessMode);
}
}else{
sessionId = generateSession(streamName, ip, tkn, protocol, sessMode);
}
}
char userPageName[NAME_BUFFER_SIZE];
snprintf(userPageName, NAME_BUFFER_SIZE, COMMS_SESSIONS, sessionId.c_str());
@ -283,36 +356,59 @@ namespace Comms{
if (!_master){
dataPage.init(userPageName, 0, false, false);
if (!dataPage){
std::string host;
Socket::hostBytesToStr(ip.data(), 16, host);
pid_t thisPid;
std::deque<std::string> args;
args.push_back(Util::getMyPath() + "MistSession");
args.push_back(sessionId);
args.push_back("--sessionmode");
args.push_back(JSON::Value(sessionMode).asString());
args.push_back("--streamname");
args.push_back(streamName);
args.push_back("--ip");
args.push_back(ip);
args.push_back("--sid");
args.push_back(sid);
args.push_back("--protocol");
args.push_back(protocol);
args.push_back("--requrl");
args.push_back(reqUrl);
// First bit defines whether to include stream name
if (sessMode & 0x08){
args.push_back("--streamname");
args.push_back(streamName);
}else{
setenv("SESSION_STREAM", streamName.c_str(), 1);
}
// Second bit defines whether to include viewer ip
if (sessMode & 0x04){
args.push_back("--ip");
args.push_back(host);
}else{
setenv("SESSION_IP", host.c_str(), 1);
}
// Third bit defines whether to include tkn
if (sessMode & 0x02){
args.push_back("--tkn");
args.push_back(tkn);
}else{
setenv("SESSION_TKN", tkn.c_str(), 1);
}
// Fourth bit defines whether to include protocol
if (sessMode & 0x01){
args.push_back("--protocol");
args.push_back(protocol);
}else{
setenv("SESSION_PROTOCOL", protocol.c_str(), 1);
}
setenv("SESSION_REQURL", reqUrl.c_str(), 1);
int err = fileno(stderr);
thisPid = Util::Procs::StartPiped(args, 0, 0, &err);
Util::Procs::forget(thisPid);
HIGH_MSG("Spawned new session executeable (pid %u) for sessionId '%s', corresponding to host %s and stream %s", thisPid, sessionId.c_str(), ip.c_str(), streamName.c_str());
unsetenv("SESSION_STREAM");
unsetenv("SESSION_IP");
unsetenv("SESSION_TKN");
unsetenv("SESSION_PROTOCOL");
unsetenv("SESSION_REQURL");
}
}
// Open SEM_SESSION
if(!sem){
char semName[NAME_BUFFER_SIZE];
snprintf(semName, NAME_BUFFER_SIZE, SEM_SESSION, sessionId.c_str());
sem.open(semName, O_RDWR, ACCESSPERMS, 1);
reload(sessionId, _master, reIssue);
if (index != INVALID_RECORD_INDEX){
setConnector(protocol);
setHost(ip);
setStream(streamName);
VERYHIGH_MSG("Reloading connection. Claimed record %lu", index);
}
Comms::reload(userPageName, COMMS_SESSIONS_INITSIZE, _master, reIssue);
VERYHIGH_MSG("Reloading connection. Claimed record %lu", index);
}
/// \brief Marks the data page as closed, so that we longer write any new data to is
@ -341,7 +437,6 @@ namespace Comms{
dataAccX.addField("host", RAX_RAW, 16);
dataAccX.addField("stream", RAX_STRING, 100);
dataAccX.addField("connector", RAX_STRING, 20);
dataAccX.addField("tags", RAX_STRING, 512);
dataAccX.addField("pktcount", RAX_64UINT);
dataAccX.addField("pktloss", RAX_64UINT);
dataAccX.addField("pktretrans", RAX_64UINT);
@ -349,7 +444,6 @@ namespace Comms{
void Connections::nullFields(){
Comms::nullFields();
setTags("");
setConnector("");
setStream("");
setHost("");
@ -373,7 +467,6 @@ namespace Comms{
host = dataAccX.getFieldAccX("host");
stream = dataAccX.getFieldAccX("stream");
connector = dataAccX.getFieldAccX("connector");
tags = dataAccX.getFieldAccX("tags");
pktcount = dataAccX.getFieldAccX("pktcount");
pktloss = dataAccX.getFieldAccX("pktloss");
pktretrans = dataAccX.getFieldAccX("pktretrans");
@ -461,14 +554,6 @@ namespace Comms{
return false;
}
std::string Connections::getTags() const{return tags.string(index);}
std::string Connections::getTags(size_t idx) const{return (master ? tags.string(idx) : 0);}
void Connections::setTags(std::string _sid){tags.set(_sid, index);}
void Connections::setTags(std::string _sid, size_t idx){
if (!master){return;}
tags.set(_sid, idx);
}
uint64_t Connections::getPacketCount() const{return pktcount.uint(index);}
uint64_t Connections::getPacketCount(size_t idx) const{
return (master ? pktcount.uint(idx) : 0);
@ -501,31 +586,32 @@ namespace Comms{
/// \brief Generates a session ID which is unique per viewer
/// \return generated session ID as string
std::string Connections::generateSession(std::string streamName, std::string ip, std::string sid, std::string connector, uint64_t sessionMode){
std::string Connections::generateSession(const std::string & streamName, const std::string & ip, const std::string & tkn, const std::string & connector, uint64_t sessionMode){
std::string concat;
std::string debugMsg = "Generating session id based on";
// First bit defines whether to include stream name
if (sessionMode > 7){
if (sessionMode & 0x08){
concat += streamName;
sessionMode -= 8;
debugMsg += " stream name '" + streamName + "'";
}
// Second bit defines whether to include viewer ip
if (sessionMode > 3){
if (sessionMode & 0x04){
concat += ip;
sessionMode -= 4;
std::string ipHex;
Socket::hostBytesToStr(ip.c_str(), ip.size(), ipHex);
debugMsg += " IP '" + ipHex + "'";
}
// Third bit defines whether to include player ip
if (sessionMode > 1){
concat += sid;
sessionMode -= 2;
// Third bit defines whether to include client-side session token
if (sessionMode & 0x02){
concat += tkn;
debugMsg += " session token '" + tkn + "'";
}
// Fourth bit defines whether to include protocol
if (sessionMode == 1){
if (sessionMode & 0x01){
concat += connector;
sessionMode = 0;
}
if (sessionMode > 0){
WARN_MSG("Could not resolve session mode of value %lu", sessionMode);
debugMsg += " protocol '" + connector + "'";
}
VERYHIGH_MSG("%s", debugMsg.c_str());
return Secure::sha256(concat.c_str(), concat.length());
}
}// namespace Comms

View file

@ -9,13 +9,21 @@
#define COMM_STATUS_REQDISCONNECT 0x10
#define COMM_STATUS_ACTIVE 0x1
#define COMM_STATUS_INVALID 0x0
#define SESS_BUNDLE_DEFAULT_VIEWER 14
#define SESS_BUNDLE_DEFAULT_OTHER 15
#define SESS_DEFAULT_STREAM_INFO_MODE 1
#define SESS_HTTP_AS_VIEWER 1
#define SESS_HTTP_AS_OUTPUT 2
#define SESS_HTTP_DISABLED 3
#define SESS_HTTP_AS_UNSPECIFIED 4
#define SESS_TKN_DEFAULT_MODE 15
#define COMM_LOOP(comm, onActive, onDisconnect) \
{\
for (size_t id = 0; id < comm.recordCount(); id++){\
if (comm.getStatus(id) == COMM_STATUS_INVALID){continue;}\
if (!Util::Procs::isRunning(comm.getPid(id))){\
if (!(comm.getStatus(id) & COMM_STATUS_DISCONNECT) && comm.getPid(id) && !Util::Procs::isRunning(comm.getPid(id))){\
comm.setStatus(COMM_STATUS_DISCONNECT | comm.getStatus(id), id);\
}\
onActive;\
@ -27,6 +35,14 @@
}
namespace Comms{
extern uint8_t sessionViewerMode;
extern uint8_t sessionInputMode;
extern uint8_t sessionOutputMode;
extern uint8_t sessionUnspecifiedMode;
extern uint8_t sessionStreamInfoMode;
extern uint8_t tknMode;
void sessionConfigCache();
class Comms{
public:
Comms();
@ -66,11 +82,13 @@ namespace Comms{
class Connections : public Comms{
public:
void reload(std::string streamName, std::string ip, std::string sid, std::string protocol, std::string reqUrl, uint64_t sessionMode, bool _master = false, bool reIssue = false);
void reload(const std::string & streamName, const std::string & ip, const std::string & tkn, const std::string & protocol, const std::string & reqUrl, bool _master = false, bool reIssue = false);
void reload(const std::string & sessId, bool _master = false, bool reIssue = false);
void unload();
operator bool() const{return dataPage.mapped && (master || index != INVALID_RECORD_INDEX);}
std::string generateSession(std::string streamName, std::string ip, std::string sid, std::string connector, uint64_t sessionMode);
std::string generateSession(const std::string & streamName, const std::string & ip, const std::string & tkn, const std::string & connector, uint64_t sessionMode);
std::string sessionId;
std::string initialTkn;
void setExit();
bool getExit();
@ -79,6 +97,8 @@ namespace Comms{
virtual void nullFields();
virtual void fieldAccess();
const std::string & getTkn() const{return initialTkn;}
uint64_t getNow() const;
uint64_t getNow(size_t idx) const;
void setNow(uint64_t _now);
@ -120,11 +140,6 @@ namespace Comms{
void setConnector(std::string _connector, size_t idx);
bool hasConnector(size_t idx, std::string protocol);
std::string getTags() const;
std::string getTags(size_t idx) const;
void setTags(std::string _sid);
void setTags(std::string _sid, size_t idx);
uint64_t getPacketCount() const;
uint64_t getPacketCount(size_t idx) const;
void setPacketCount(uint64_t _count);
@ -197,5 +212,10 @@ namespace Comms{
virtual void addFields();
virtual void nullFields();
virtual void fieldAccess();
std::string getTags() const;
std::string getTags(size_t idx) const;
void setTags(std::string _sid);
void setTags(std::string _sid, size_t idx);
};
}// namespace Comms

View file

@ -231,7 +231,7 @@ static inline void show_stackframe(){}
#define SEM_TRACKLIST "/MstTRKS%s" //%s stream name
#define SEM_SESSION "MstSess%s"
#define SEM_SESSCACHE "/MstSessCacheLock"
#define SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID 14
#define SESS_TIMEOUT 600 // Session timeout in seconds
#define SHM_CAPA "MstCapa"
#define SHM_PROTO "MstProt"
#define SHM_PROXY "MstProx"

View file

@ -274,7 +274,7 @@ namespace HLS{
if (trackData.mediaFormat == ".ts"){return;}
result << "#EXT-X-MAP:URI=\"" << trackData.urlPrefix << "init" << trackData.mediaFormat;
if (trackData.sessionId.size()){result << "?sessId=" << trackData.sessionId;}
if (trackData.sessionId.size()){result << "?tkn=" << trackData.sessionId;}
result << "\"\r\n";
}
@ -327,7 +327,7 @@ namespace HLS{
result << "?msn=" << fragData.currentFrag;
result << "&mTrack=" << trackData.timingTrackId;
result << "&dur=" << fragData.duration;
if (trackData.sessionId.size()){result << "&sessId=" << trackData.sessionId;}
if (trackData.sessionId.size()){result << "&tkn=" << trackData.sessionId;}
result << "\r\n";
}
@ -341,7 +341,7 @@ namespace HLS{
result << "?msn=" << fragData.currentFrag;
result << "&mTrack=" << trackData.timingTrackId;
result << "&dur=" << duration;
if (trackData.sessionId.size()){result << "&sessId=" << trackData.sessionId;}
if (trackData.sessionId.size()){result << "&tkn=" << trackData.sessionId;}
result << "\"";
// NOTE: INDEPENDENT tags, specified ONLY for VIDEO tracks, indicate the first partial fragment
@ -448,7 +448,7 @@ namespace HLS{
result << "?msn=" << fragData.currentFrag - 1;
result << "&mTrack=" << trackData.timingTrackId;
result << "&dur=" << partDurationMaxMs;
if (trackData.sessionId.size()){result << "&sessId=" << trackData.sessionId;}
if (trackData.sessionId.size()){result << "&tkn=" << trackData.sessionId;}
result << "\"\r\n";
}
@ -509,7 +509,7 @@ namespace HLS{
result << ",NAME=\"" << name << "\",URI=\"" << trackId << "/index.m3u8";
result << "?mTrack=" << masterData.mainTrack;
result << "&iMsn=" << iFrag;
if (masterData.hasSessId){result << "&sessId=" << masterData.sessId;}
if (masterData.sessId.size()){result << "&tkn=" << masterData.sessId;}
if (masterData.noLLHLS){result << "&llhls=0";}
result << "\"\r\n";
}
@ -529,7 +529,7 @@ namespace HLS{
result << "/index.m3u8";
result << "?mTrack=" << masterData.mainTrack;
result << "&iMsn=" << iFrag;
if (masterData.hasSessId){result << "&sessId=" << masterData.sessId;}
if (masterData.sessId.size()){result << "&tkn=" << masterData.sessId;}
if (masterData.noLLHLS){result << "&llhls=0";}
result << "\r\n";
}

View file

@ -742,13 +742,13 @@ bool HTTP::Parser::parse(std::string &HTTPbuffer, Util::DataCallback &cb){
/// HTTP variable parser to std::map<std::string, std::string> structure.
/// Reads variables from data, decodes and stores them to storage.
void HTTP::parseVars(const std::string &data, std::map<std::string, std::string> &storage){
void HTTP::parseVars(const std::string &data, std::map<std::string, std::string> &storage, const std::string & separator){
std::string varname;
std::string varval;
// position where a part starts (e.g. after &)
size_t pos = 0;
while (pos < data.length()){
size_t nextpos = data.find('&', pos);
size_t nextpos = data.find(separator, pos);
if (nextpos == std::string::npos){nextpos = data.length();}
size_t eq_pos = data.find('=', pos);
if (eq_pos < nextpos){
@ -769,7 +769,7 @@ void HTTP::parseVars(const std::string &data, std::map<std::string, std::string>
break;
}
// erase &
pos = nextpos + 1;
pos = nextpos + separator.size();
}
}

View file

@ -14,7 +14,7 @@ namespace HTTP{
/// HTTP variable parser to std::map<std::string, std::string> structure.
/// Reads variables from data, decodes and stores them to storage.
void parseVars(const std::string &data, std::map<std::string, std::string> &storage);
void parseVars(const std::string &data, std::map<std::string, std::string> &storage, const std::string & separator = "&");
/// Simple class for reading and writing HTTP 1.0 and 1.1.
class Parser : public Util::DataCallback{

View file

@ -166,6 +166,8 @@ bool Socket::isBinAddress(const std::string &binAddr, std::string addr){
/// Converts the given address with optional subnet to binary IPv6 form.
/// Returns 16 bytes of address, followed by 1 byte of subnet bits, zero or more times.
std::string Socket::getBinForms(std::string addr){
// Check for empty address
if (!addr.size()){return std::string(17, (char)0);}
// Check if we need to do prefix matching
uint8_t prefixLen = 128;
if (addr.find('/') != std::string::npos){
@ -1796,6 +1798,14 @@ void Socket::UDPConnection::GetDestination(std::string &destIp, uint32_t &port){
FAIL_MSG("Could not get destination for UDP socket");
}// Socket::UDPConnection GetDestination
/// Gets the properties of the receiving end of this UDP socket.
/// This will be the receiving end for all SendNow calls.
std::string Socket::UDPConnection::getBinDestination(){
std::string binList = getIPv6BinAddr(*(sockaddr_in6*)destAddr);
if (binList.size() < 16){ return std::string("\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", 16); }
return binList.substr(0, 16);
}// Socket::UDPConnection GetDestination
/// Returns the port number of the receiving end of this socket.
/// Returns 0 on error.
uint32_t Socket::UDPConnection::getDestPort() const{

View file

@ -215,6 +215,7 @@ namespace Socket{
void setBlocking(bool blocking);
void SetDestination(std::string hostname, uint32_t port);
void GetDestination(std::string &hostname, uint32_t &port);
std::string getBinDestination();
const void * getDestAddr(){return destAddr;}
size_t getDestAddrLen(){return destAddr_size;}
std::string getBoundAddress();

View file

@ -73,31 +73,31 @@ namespace HTTP{
}
/// Takes an incoming HTTP::Parser request for a Websocket, and turns it into one.
Websocket::Websocket(Socket::Connection &c, HTTP::Parser &h) : C(c){
Websocket::Websocket(Socket::Connection &c, const HTTP::Parser &req, HTTP::Parser &resp) : C(c){
frameType = 0;
maskOut = false;
std::string connHeader = h.GetHeader("Connection");
std::string connHeader = req.GetHeader("Connection");
Util::stringToLower(connHeader);
if (connHeader.find("upgrade") == std::string::npos){
FAIL_MSG("Could not negotiate websocket, connection header incorrect (%s).", connHeader.c_str());
C.close();
return;
}
std::string upgradeHeader = h.GetHeader("Upgrade");
std::string upgradeHeader = req.GetHeader("Upgrade");
Util::stringToLower(upgradeHeader);
if (upgradeHeader != "websocket"){
FAIL_MSG("Could not negotiate websocket, upgrade header incorrect (%s).", upgradeHeader.c_str());
C.close();
return;
}
if (h.GetHeader("Sec-WebSocket-Version") != "13"){
if (req.GetHeader("Sec-WebSocket-Version") != "13"){
FAIL_MSG("Could not negotiate websocket, version incorrect (%s).",
h.GetHeader("Sec-WebSocket-Version").c_str());
req.GetHeader("Sec-WebSocket-Version").c_str());
C.close();
return;
}
#ifdef SSL
std::string client_key = h.GetHeader("Sec-WebSocket-Key");
std::string client_key = req.GetHeader("Sec-WebSocket-Key");
if (!client_key.size()){
FAIL_MSG("Could not negotiate websocket, missing key!");
C.close();
@ -105,15 +105,13 @@ namespace HTTP{
}
#endif
h.Clean();
h.setCORSHeaders();
h.SetHeader("Upgrade", "websocket");
h.SetHeader("Connection", "Upgrade");
resp.SetHeader("Upgrade", "websocket");
resp.SetHeader("Connection", "Upgrade");
#ifdef SSL
h.SetHeader("Sec-WebSocket-Accept", calculateKeyAccept(client_key));
resp.SetHeader("Sec-WebSocket-Accept", calculateKeyAccept(client_key));
#endif
// H.SetHeader("Sec-WebSocket-Protocol", "json");
h.SendResponse("101", "Websocket away!", C);
resp.SendResponse("101", "Websocket away!", C);
}
/// Loops calling readFrame until the connection is closed, sleeping in between reads if needed.

View file

@ -7,7 +7,7 @@
namespace HTTP{
class Websocket{
public:
Websocket(Socket::Connection &c, HTTP::Parser &h);
Websocket(Socket::Connection &c, const HTTP::Parser &req, HTTP::Parser &resp);
Websocket(Socket::Connection &c, const HTTP::URL & url, std::map<std::string, std::string> * headers = 0);
Websocket(Socket::Connection &c, bool client);
operator bool() const;