mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
275 lines
9.2 KiB
C++
275 lines
9.2 KiB
C++
/*
|
|
* 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_DATA_MESSAGE_HPP
|
|
#define WEBSOCKET_DATA_MESSAGE_HPP
|
|
|
|
#include "../common.hpp"
|
|
#include "../utf8_validator/utf8_validator.hpp"
|
|
#include "../processors/hybi_util.hpp"
|
|
|
|
#include <boost/detail/atomic_count.hpp>
|
|
#include <boost/enable_shared_from_this.hpp>
|
|
#include <boost/function.hpp>
|
|
#include <boost/intrusive_ptr.hpp>
|
|
#include <boost/version.hpp>
|
|
#if BOOST_VERSION >= 105300 // 1.53.00
|
|
#include <boost/thread/lock_guard.hpp>
|
|
#endif
|
|
#include <boost/thread/mutex.hpp>
|
|
#include <boost/thread/recursive_mutex.hpp>
|
|
#include <boost/utility.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <queue>
|
|
#include <vector>
|
|
|
|
namespace websocketpp {
|
|
namespace message {
|
|
|
|
/// message::pool impliments a reference counted pool of elements.
|
|
|
|
// element_type interface:
|
|
// constructor(ptr p, size_t index)
|
|
// - shared pointer to the managing pool
|
|
// - integer index
|
|
|
|
// get_index();
|
|
// set_live()
|
|
|
|
template <class element_type>
|
|
class pool : public boost::enable_shared_from_this< pool<element_type> >,
|
|
boost::noncopyable {
|
|
public:
|
|
typedef pool<element_type> type;
|
|
typedef boost::shared_ptr<type> ptr;
|
|
typedef boost::weak_ptr<type> weak_ptr;
|
|
typedef typename element_type::ptr element_ptr;
|
|
typedef boost::function<void()> callback_type;
|
|
|
|
pool(size_t max_elements) : m_cur_elements(0),m_max_elements(max_elements) {}
|
|
~pool() {}
|
|
|
|
// copy/assignment constructors require C++11
|
|
// boost::noncopyable is being used in the meantime.
|
|
// pool(pool const&) = delete;
|
|
// pool& operator=(pool const&) = delete
|
|
|
|
/// Requests a pointer to the next free element in the resource pool.
|
|
/* If there isn't a free element a new one is created. If the maximum number
|
|
* of elements has been created then it returns an empty/null element
|
|
* pointer.
|
|
*/
|
|
element_ptr get() {
|
|
element_ptr p, q;
|
|
{
|
|
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
|
|
|
/*std::cout << "message requested ("
|
|
<< m_cur_elements-m_avaliable.size()
|
|
<< "/"
|
|
<< m_cur_elements
|
|
<< ")"
|
|
<< std::endl;*/
|
|
|
|
if (!m_avaliable.empty()) {
|
|
p = m_avaliable.front(); // FIXME can call intrusive_ptr_add_ref (line 215) which can deadlock
|
|
q = p;
|
|
m_avaliable.pop(); // FIXME can call intrusive_ptr_release (line 217) which can deadlock
|
|
m_used[p->get_index()] = p;
|
|
} else {
|
|
if (m_cur_elements == m_max_elements) {
|
|
return element_ptr();
|
|
}
|
|
|
|
p = element_ptr(new element_type(type::shared_from_this(),m_cur_elements));
|
|
m_cur_elements++;
|
|
m_used.push_back(p);
|
|
|
|
/*std::cout << "Allocated new data message. Count is now "
|
|
<< m_cur_elements
|
|
<< std::endl;*/
|
|
}
|
|
|
|
p->set_live();
|
|
}
|
|
return p;
|
|
}
|
|
void recycle(element_ptr p) {
|
|
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
|
|
|
if (p->get_index()+1 > m_used.size() || m_used[p->get_index()] != p) {
|
|
//std::cout << "error tried to recycle a pointer we don't control" << std::endl;
|
|
// error tried to recycle a pointer we don't control
|
|
return;
|
|
}
|
|
|
|
m_avaliable.push(p);
|
|
m_used[p->get_index()] = element_ptr();
|
|
|
|
/*std::cout << "message recycled ("
|
|
<< m_cur_elements-m_avaliable.size()
|
|
<< "/"
|
|
<< m_cur_elements
|
|
<< ")"
|
|
<< std::endl;*/
|
|
|
|
if (m_callback && m_avaliable.size() == 1) {
|
|
m_callback();
|
|
}
|
|
}
|
|
|
|
// set a function that will be called when new elements are avaliable.
|
|
void set_callback(callback_type fn) {
|
|
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
|
m_callback = fn;
|
|
}
|
|
|
|
private:
|
|
size_t m_cur_elements;
|
|
size_t m_max_elements;
|
|
|
|
std::queue<element_ptr> m_avaliable;
|
|
std::vector<element_ptr> m_used;
|
|
|
|
callback_type m_callback;
|
|
|
|
boost::recursive_mutex m_lock;
|
|
};
|
|
|
|
class data {
|
|
public:
|
|
typedef boost::intrusive_ptr<data> ptr;
|
|
typedef pool<data>::ptr pool_ptr;
|
|
typedef pool<data>::weak_ptr pool_weak_ptr;
|
|
|
|
data(pool_ptr p, size_t s);
|
|
|
|
void reset(websocketpp::frame::opcode::value opcode);
|
|
|
|
frame::opcode::value get_opcode() const;
|
|
const std::string& get_payload() const;
|
|
const std::string& get_header() const;
|
|
|
|
// ##reading##
|
|
// sets the masking key to be used to unmask as bytes are read.
|
|
void set_masking_key(int32_t key);
|
|
|
|
// read at most size bytes from a payload stream and perform unmasking/utf8
|
|
// validation. Returns number of bytes read.
|
|
// throws a processor::exception if the message is too big, there is a fatal
|
|
// istream read error, or invalid UTF8 data is read for a text message
|
|
//uint64_t process_payload(std::istream& input,uint64_t size);
|
|
void process_payload(char * input, size_t size);
|
|
void complete();
|
|
void validate_payload();
|
|
|
|
// ##writing##
|
|
// sets the payload to payload. Performs max size and UTF8 validation
|
|
// immediately and throws processor::exception if it fails
|
|
void set_payload(const std::string& payload);
|
|
void append_payload(const std::string& payload);
|
|
|
|
void set_header(const std::string& header);
|
|
|
|
// Performs masking and header generation if it has not been done already.
|
|
void set_prepared(bool b);
|
|
bool get_prepared() const;
|
|
void mask();
|
|
|
|
int32_t get_masking_key() const {
|
|
return m_masking_key.i;
|
|
}
|
|
|
|
// pool management interface
|
|
void set_live();
|
|
size_t get_index() const;
|
|
private:
|
|
static const uint64_t PAYLOAD_SIZE_INIT = 1000; // 1KB
|
|
static const uint64_t PAYLOAD_SIZE_MAX = 100000000;// 100MB
|
|
|
|
typedef websocketpp::processor::hybi_util::masking_key_type masking_key_type;
|
|
|
|
friend void intrusive_ptr_add_ref(const data * s) {
|
|
boost::unique_lock<boost::recursive_mutex> lock(s->m_lock);
|
|
++s->m_ref_count;
|
|
}
|
|
|
|
friend void intrusive_ptr_release(const data * s) {
|
|
boost::unique_lock<boost::recursive_mutex> lock(s->m_lock);
|
|
|
|
// TODO: thread safety
|
|
long count = --s->m_ref_count;
|
|
if (count == 1 && s->m_live) {
|
|
// recycle if endpoint exists
|
|
s->m_live = false;
|
|
|
|
pool_ptr pp = s->m_pool.lock();
|
|
if (pp) {
|
|
lock.unlock();
|
|
pp->recycle(ptr(const_cast<data *>(s)));
|
|
}
|
|
|
|
//s->m_pool->recycle(ptr(const_cast<data *>(s)));
|
|
} else if (count == 0) {
|
|
lock.unlock();
|
|
boost::checked_delete(static_cast<data const *>(s));
|
|
}
|
|
}
|
|
|
|
// Message state
|
|
frame::opcode::value m_opcode;
|
|
|
|
// UTF8 validation state
|
|
utf8_validator::validator m_validator;
|
|
|
|
// Masking state
|
|
masking_key_type m_masking_key;
|
|
bool m_masked;
|
|
size_t m_prepared_key;
|
|
|
|
std::string m_header;
|
|
std::string m_payload;
|
|
|
|
bool m_prepared;
|
|
|
|
// reference counting
|
|
size_t m_index;
|
|
mutable boost::detail::atomic_count m_ref_count;
|
|
mutable pool_weak_ptr m_pool;
|
|
mutable bool m_live;
|
|
mutable boost::recursive_mutex m_lock;
|
|
};
|
|
|
|
typedef boost::intrusive_ptr<data> data_ptr;
|
|
|
|
} // namespace message
|
|
} // namespace websocketpp
|
|
|
|
#endif // WEBSOCKET_DATA_MESSAGE_HPP
|