28#ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP
29#define WEBSOCKETPP_CONNECTION_IMPL_HPP
31#include <websocketpp/processors/hybi00.hpp>
32#include <websocketpp/processors/hybi07.hpp>
33#include <websocketpp/processors/hybi08.hpp>
34#include <websocketpp/processors/hybi13.hpp>
36#include <websocketpp/processors/processor.hpp>
38#include <websocketpp/common/platforms.hpp>
39#include <websocketpp/common/system_error.hpp>
50namespace istate = session::internal_state;
52template <
typename config>
54 termination_handler new_handler)
57 "connection set_termination_handler");
61 m_termination_handler = new_handler;
64template <
typename config>
67 return m_processor->get_origin(m_request);
70template <
typename config>
73 return m_send_buffer_size;
76template <
typename config>
82template <
typename config>
84 frame::opcode::value op)
86 message_ptr msg = m_msg_manager->get_message(op,payload.size());
87 msg->append_payload(payload);
88 msg->set_compressed(
true);
93template <
typename config>
95 frame::opcode::value op)
97 message_ptr msg = m_msg_manager->get_message(op,len);
98 msg->append_payload(payload,len);
103template <
typename config>
111 scoped_lock_type lock(m_connection_state_lock);
112 if (m_state != session::state::open) {
117 message_ptr outgoing_msg;
118 bool needs_writing =
false;
120 if (msg->get_prepared()) {
123 scoped_lock_type lock(m_write_lock);
124 write_push(outgoing_msg);
125 needs_writing = !m_write_flag && !m_send_queue.empty();
127 outgoing_msg = m_msg_manager->get_message();
133 scoped_lock_type lock(m_write_lock);
134 lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg);
140 write_push(outgoing_msg);
141 needs_writing = !m_write_flag && !m_send_queue.empty();
145 transport_con_type::dispatch(lib::bind(
151 return lib::error_code();
154template <
typename config>
161 scoped_lock_type lock(m_connection_state_lock);
162 if (m_state != session::state::open) {
163 std::stringstream ss;
164 ss <<
"connection::ping called from invalid state " << m_state;
171 message_ptr msg = m_msg_manager->get_message();
177 ec = m_processor->prepare_ping(payload,msg);
181 if (m_pong_timeout_handler) {
184 m_ping_timer->cancel();
187 if (m_pong_timeout_dur > 0) {
188 m_ping_timer = transport_con_type::set_timer(
191 &type::handle_pong_timeout,
194 lib::placeholders::_1
202 set but the transport in use does not support timeouts.");
206 bool needs_writing =
false;
208 scoped_lock_type lock(m_write_lock);
210 needs_writing = !m_write_flag && !m_send_queue.empty();
214 transport_con_type::dispatch(lib::bind(
220 ec = lib::error_code();
223template<
typename config>
232template<
typename config>
234 lib::error_code
const & ec)
246 if (m_pong_timeout_handler) {
247 m_pong_timeout_handler(m_connection_hdl,payload);
251template <
typename config>
258 scoped_lock_type lock(m_connection_state_lock);
259 if (m_state != session::state::open) {
260 std::stringstream ss;
261 ss <<
"connection::pong called from invalid state " << m_state;
268 message_ptr msg = m_msg_manager->get_message();
274 ec = m_processor->prepare_pong(payload,msg);
277 bool needs_writing =
false;
279 scoped_lock_type lock(m_write_lock);
281 needs_writing = !m_write_flag && !m_send_queue.empty();
285 transport_con_type::dispatch(lib::bind(
291 ec = lib::error_code();
294template<
typename config>
303template <
typename config>
305 std::string
const & reason, lib::error_code & ec)
312 std::string tr(reason,0,std::min<size_t>(reason.size(),
315 scoped_lock_type lock(m_connection_state_lock);
317 if (m_state != session::state::open) {
325template<
typename config>
327 std::string
const & reason)
330 close(code,reason,ec);
340template <
typename config>
343 return transport_con_type::interrupt(
345 &type::handle_interrupt,
352template <
typename config>
354 if (m_interrupt_handler) {
355 m_interrupt_handler(m_connection_hdl);
359template <
typename config>
362 return transport_con_type::dispatch(
364 &type::handle_pause_reading,
371template <
typename config>
377template <
typename config>
380 return transport_con_type::dispatch(
382 &type::handle_resume_reading,
389template <
typename config>
405template <
typename config>
408 return m_uri->get_secure();
411template <
typename config>
414 return m_uri->get_host();
417template <
typename config>
420 return m_uri->get_resource();
423template <
typename config>
426 return m_uri->get_port();
429template <
typename config>
435template <
typename config>
446template <
typename config>
448 return m_subprotocol;
451template <
typename config>
452std::vector<std::string>
const &
454 return m_requested_subprotocols;
457template <
typename config>
459 lib::error_code & ec)
467 if (value.empty() || std::find_if(value.begin(),value.end(),
474 m_requested_subprotocols.push_back(value);
477template <
typename config>
480 this->add_subprotocol(value,ec);
487template <
typename config>
489 lib::error_code & ec)
497 ec = lib::error_code();
501 std::vector<std::string>::iterator it;
503 it = std::find(m_requested_subprotocols.begin(),
504 m_requested_subprotocols.end(),
507 if (it == m_requested_subprotocols.end()) {
512 m_subprotocol = value;
515template <
typename config>
518 this->select_subprotocol(value,ec);
525template <
typename config>
528 return m_request.get_header(key);
531template <
typename config>
534 return m_request.get_body();
537template <
typename config>
540 return m_response.get_header(key);
544template <
typename config>
547 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
548 throw exception(
"Call to set_status from invalid state",
551 m_response.set_status(code);
555template <
typename config>
557 std::string
const & msg)
559 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
560 throw exception(
"Call to set_status from invalid state",
564 m_response.set_status(code,msg);
568template <
typename config>
570 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
571 throw exception(
"Call to set_status from invalid state",
575 m_response.set_body(value);
579template <
typename config>
581 std::string
const & val)
584 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
586 m_response.append_header(key,val);
588 throw exception(
"Call to append_header from invalid state",
592 if (m_internal_state == istate::USER_INIT) {
594 m_request.append_header(key,val);
596 throw exception(
"Call to append_header from invalid state",
603template <
typename config>
605 std::string
const & val)
608 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
610 m_response.replace_header(key,val);
612 throw exception(
"Call to replace_header from invalid state",
616 if (m_internal_state == istate::USER_INIT) {
618 m_request.replace_header(key,val);
620 throw exception(
"Call to replace_header from invalid state",
627template <
typename config>
631 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
633 m_response.remove_header(key);
635 throw exception(
"Call to remove_header from invalid state",
639 if (m_internal_state == istate::USER_INIT) {
641 m_request.remove_header(key);
643 throw exception(
"Call to remove_header from invalid state",
660template <
typename config>
664 if (m_handshake_timer) {
665 m_handshake_timer->cancel();
666 m_handshake_timer.reset();
670 m_http_state = session::http_state::deferred;
672 return lib::error_code();
685template <
typename config>
688 scoped_lock_type lock(m_connection_state_lock);
689 if (m_http_state != session::http_state::deferred) {
694 m_http_state = session::http_state::body_written;
697 this->write_http_response(lib::error_code());
698 ec = lib::error_code();
701template <
typename config>
704 this->send_http_response(ec);
715template <
typename config>
719 if (m_internal_state != istate::USER_INIT) {
725 m_internal_state = istate::TRANSPORT_INIT;
730 transport_con_type::init(
732 &type::handle_transport_init,
734 lib::placeholders::_1
739template <
typename config>
743 lib::error_code ecm = ec;
745 if (m_internal_state != istate::TRANSPORT_INIT) {
747 "handle_transport_init must be called from transport init state");
753 s <<
"handle_transport_init received error: "<< ecm.message();
756 this->terminate(ecm);
762 m_internal_state = istate::READ_HTTP_REQUEST;
763 this->read_handshake(1);
767 m_internal_state = istate::WRITE_HTTP_REQUEST;
768 m_processor = get_processor(config::client_version);
769 this->send_http_request();
773template <
typename config>
774void connection<config>::read_handshake(
size_t num_bytes) {
777 if (m_open_handshake_timeout_dur > 0) {
778 m_handshake_timer = transport_con_type::set_timer(
779 m_open_handshake_timeout_dur,
781 &type::handle_open_handshake_timeout,
783 lib::placeholders::_1
788 transport_con_type::async_read_at_least(
791 config::connection_read_buffer_size,
793 &type::handle_read_handshake,
795 lib::placeholders::_1,
796 lib::placeholders::_2
803template <
typename config>
804void connection<config>::handle_read_handshake(lib::error_code
const & ec,
805 size_t bytes_transferred)
809 lib::error_code ecm = ec;
812 scoped_lock_type lock(m_connection_state_lock);
814 if (m_state == session::state::connecting) {
815 if (m_internal_state != istate::READ_HTTP_REQUEST) {
818 }
else if (m_state == session::state::closed) {
823 "handle_read_handshake invoked after connection was closed");
834 "got (expected) eof/state error from closed con");
839 this->terminate(ecm);
844 if (bytes_transferred > config::connection_read_buffer_size) {
850 size_t bytes_processed = 0;
852 bytes_processed = m_request.consume(m_buf,bytes_transferred);
853 }
catch (http::exception &e) {
856 m_response.set_status(e.m_error_code,e.m_error_msg);
863 if (bytes_processed > bytes_transferred) {
871 s <<
"bytes_transferred: " << bytes_transferred
872 <<
" bytes, bytes processed: " << bytes_processed <<
" bytes";
876 if (m_request.ready()) {
877 lib::error_code processor_ec = this->initialize_processor();
879 this->write_http_response_error(processor_ec);
883 if (m_processor && m_processor->get_version() == 0) {
886 if (bytes_transferred-bytes_processed >= 8) {
887 m_request.replace_header(
888 "Sec-WebSocket-Key3",
889 std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
891 bytes_processed += 8;
895 m_response.set_status(http::status_code::internal_server_error);
903 if (!m_request.get_header(
"Sec-WebSocket-Key3").empty()) {
912 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
913 m_buf_cursor = bytes_transferred-bytes_processed;
916 m_internal_state = istate::PROCESS_HTTP_REQUEST;
919 lib::error_code handshake_ec = this->process_handshake_request();
924 if (!m_is_http || m_http_state == session::http_state::init) {
925 this->write_http_response(handshake_ec);
929 transport_con_type::async_read_at_least(
932 config::connection_read_buffer_size,
934 &type::handle_read_handshake,
936 lib::placeholders::_1,
937 lib::placeholders::_2
948template <
typename config>
949void connection<config>::write_http_response_error(lib::error_code
const & ec) {
950 if (m_internal_state != istate::READ_HTTP_REQUEST) {
952 "write_http_response_error called in invalid state");
957 m_internal_state = istate::PROCESS_HTTP_REQUEST;
959 this->write_http_response(ec);
964template <
typename config>
965void connection<config>::handle_read_frame(lib::error_code
const & ec,
966 size_t bytes_transferred)
970 lib::error_code ecm = ec;
972 if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
980 if (m_state == session::state::closed) {
985 }
else if (m_state == session::state::closing && !m_is_server) {
989 terminate(lib::error_code());
997 if (m_state == session::state::closed) {
999 "handle_read_frame: got invalid istate in closed state");
1012 log_err(echannel,
"handle_read_frame", ecm);
1013 this->terminate(ecm);
1027 std::stringstream s;
1028 s <<
"p = " << p <<
" bytes transferred = " << bytes_transferred;
1032 while (p < bytes_transferred) {
1034 std::stringstream s;
1035 s <<
"calling consume with " << bytes_transferred-p <<
" bytes";
1039 lib::error_code consume_ec;
1042 std::stringstream s;
1043 s <<
"Processing Bytes: " <<
utility::to_hex(
reinterpret_cast<uint8_t*
>(m_buf)+p,bytes_transferred-p);
1047 p += m_processor->consume(
1048 reinterpret_cast<uint8_t*
>(m_buf)+p,
1049 bytes_transferred-p,
1054 std::stringstream s;
1055 s <<
"bytes left after consume: " << bytes_transferred-p;
1061 if (config::drop_on_protocol_error) {
1062 this->terminate(consume_ec);
1065 lib::error_code close_ec;
1068 consume_ec.message(),
1074 this->terminate(close_ec);
1081 if (m_processor->ready()) {
1083 std::stringstream s;
1084 s <<
"Complete message received. Dispatching";
1088 message_ptr msg = m_processor->get_message();
1094 if (m_state != session::state::open) {
1096 }
else if (m_message_handler) {
1097 m_message_handler(m_connection_hdl, msg);
1100 process_control_frame(msg);
1109template <
typename config>
1115 transport_con_type::async_read_at_least(
1125 config::connection_read_buffer_size,
1130template <
typename config>
1136 return lib::error_code();
1143 m_response.set_status(http::status_code::bad_request);
1147 m_processor = get_processor(version);
1151 return lib::error_code();
1157 m_response.set_status(http::status_code::bad_request);
1159 std::stringstream ss;
1161 std::vector<int>::const_iterator it;
1168 m_response.replace_header(
"Sec-WebSocket-Version",ss.str());
1172template <
typename config>
1183 (transport_con_type::is_secure() ?
"https" :
"http")
1186 if (!m_uri->get_valid()) {
1188 m_response.set_status(http::status_code::bad_request);
1192 if (m_http_handler) {
1194 m_http_handler(m_connection_hdl);
1196 if (m_state == session::state::closed) {
1200 set_status(http::status_code::upgrade_required);
1204 return lib::error_code();
1207 lib::error_code ec = m_processor->validate_handshake(m_request);
1213 m_response.set_status(http::status_code::bad_request);
1219 std::pair<lib::error_code,std::string> neg_results;
1220 neg_results = m_processor->negotiate_extensions(m_request);
1225 m_elog->write(
log::elevel::info,
"Bad request: " + neg_results.first.message());
1226 m_response.set_status(http::status_code::bad_request);
1227 return neg_results.first;
1228 }
else if (neg_results.first) {
1233 "Extension negotiation failed: " + neg_results.first.message());
1238 if (neg_results.second.size() > 0) {
1239 m_response.replace_header(
"Sec-WebSocket-Extensions",
1240 neg_results.second);
1245 m_uri = m_processor->get_uri(m_request);
1248 if (!m_uri->get_valid()) {
1250 m_response.set_status(http::status_code::bad_request);
1255 lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1256 m_requested_subprotocols);
1263 if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1264 m_response.set_status(http::status_code::switching_protocols);
1268 ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1271 std::stringstream s;
1272 s <<
"Processing error: " << ec <<
"(" << ec.message() <<
")";
1275 m_response.set_status(http::status_code::internal_server_error);
1285 if (m_response.get_status_code() == http::status_code::uninitialized) {
1286 m_response.set_status(http::status_code::bad_request);
1292 return lib::error_code();
1295template <
typename config>
1304 if (m_response.get_status_code() == http::status_code::uninitialized) {
1305 m_response.
set_status(http::status_code::internal_server_error);
1311 m_response.set_version(
"HTTP/1.1");
1314 if (m_response.get_header(
"Server").empty()) {
1315 if (!m_user_agent.empty()) {
1316 m_response.replace_header(
"Server",m_user_agent);
1318 m_response.remove_header(
"Server");
1324 m_handshake_buffer = m_processor->get_raw(m_response);
1327 m_handshake_buffer = m_response.raw();
1332 if (!m_response.get_header(
"Sec-WebSocket-Key3").empty()) {
1339 transport_con_type::async_write(
1340 m_handshake_buffer.data(),
1341 m_handshake_buffer.size(),
1343 &type::handle_write_http_response,
1345 lib::placeholders::_1
1350template <
typename config>
1351void connection<config>::handle_write_http_response(lib::error_code
const & ec) {
1354 lib::error_code ecm = ec;
1357 scoped_lock_type lock(m_connection_state_lock);
1359 if (m_state == session::state::connecting) {
1360 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1363 }
else if (m_state == session::state::closed) {
1368 "handle_write_http_response invoked after connection was closed");
1379 "got (expected) eof/state error from closed con");
1384 this->terminate(ecm);
1388 if (m_handshake_timer) {
1389 m_handshake_timer->cancel();
1390 m_handshake_timer.reset();
1393 if (m_response.get_status_code() != http::status_code::switching_protocols)
1400 std::stringstream s;
1401 s <<
"Handshake ended with HTTP error: "
1402 << m_response.get_status_code();
1408 this->log_http_result();
1412 "got to writing HTTP results with m_ec set: "+m_ec.message());
1417 this->terminate(m_ec);
1421 this->log_open_result();
1423 m_internal_state = istate::PROCESS_CONNECTION;
1424 m_state = session::state::open;
1426 if (m_open_handler) {
1427 m_open_handler(m_connection_hdl);
1430 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1433template <
typename config>
1434void connection<config>::send_http_request() {
1443 ec = m_processor->client_handshake_request(m_request,m_uri,
1444 m_requested_subprotocols);
1456 if (m_request.get_header(
"User-Agent").empty()) {
1457 if (!m_user_agent.empty()) {
1458 m_request.replace_header(
"User-Agent",m_user_agent);
1460 m_request.remove_header(
"User-Agent");
1464 m_handshake_buffer = m_request.raw();
1470 if (m_open_handshake_timeout_dur > 0) {
1471 m_handshake_timer = transport_con_type::set_timer(
1472 m_open_handshake_timeout_dur,
1474 &type::handle_open_handshake_timeout,
1476 lib::placeholders::_1
1481 transport_con_type::async_write(
1482 m_handshake_buffer.data(),
1483 m_handshake_buffer.size(),
1485 &type::handle_send_http_request,
1487 lib::placeholders::_1
1492template <
typename config>
1493void connection<config>::handle_send_http_request(lib::error_code
const & ec) {
1496 lib::error_code ecm = ec;
1499 scoped_lock_type lock(m_connection_state_lock);
1501 if (m_state == session::state::connecting) {
1502 if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1505 m_internal_state = istate::READ_HTTP_RESPONSE;
1507 }
else if (m_state == session::state::closed) {
1512 "handle_send_http_request invoked after connection was closed");
1523 "got (expected) eof/state error from closed con");
1528 this->terminate(ecm);
1532 transport_con_type::async_read_at_least(
1535 config::connection_read_buffer_size,
1537 &type::handle_read_http_response,
1539 lib::placeholders::_1,
1540 lib::placeholders::_2
1545template <
typename config>
1546void connection<config>::handle_read_http_response(lib::error_code
const & ec,
1547 size_t bytes_transferred)
1551 lib::error_code ecm = ec;
1554 scoped_lock_type lock(m_connection_state_lock);
1556 if (m_state == session::state::connecting) {
1557 if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1560 }
else if (m_state == session::state::closed) {
1565 "handle_read_http_response invoked after connection was closed");
1576 "got (expected) eof/state error from closed con");
1581 this->terminate(ecm);
1585 size_t bytes_processed = 0;
1588 bytes_processed = m_response.consume(m_buf,bytes_transferred);
1589 }
catch (http::exception & e) {
1591 std::string(
"error in handle_read_http_response: ")+e.what());
1598 if (m_response.headers_ready()) {
1599 if (m_handshake_timer) {
1600 m_handshake_timer->cancel();
1601 m_handshake_timer.reset();
1604 lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1610 this->terminate(validate_ec);
1616 std::pair<lib::error_code,std::string> neg_results;
1617 neg_results = m_processor->negotiate_extensions(m_response);
1619 if (neg_results.first) {
1628 + neg_results.first.message());
1634 m_internal_state = istate::PROCESS_CONNECTION;
1635 m_state = session::state::open;
1637 this->log_open_result();
1639 if (m_open_handler) {
1640 m_open_handler(m_connection_hdl);
1646 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1647 m_buf_cursor = bytes_transferred-bytes_processed;
1649 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1651 transport_con_type::async_read_at_least(
1654 config::connection_read_buffer_size,
1656 &type::handle_read_http_response,
1658 lib::placeholders::_1,
1659 lib::placeholders::_2
1665template <
typename config>
1666void connection<config>::handle_open_handshake_timeout(
1667 lib::error_code
const & ec)
1673 "open handle_open_handshake_timeout error: "+ec.message());
1681template <
typename config>
1682void connection<config>::handle_close_handshake_timeout(
1683 lib::error_code
const & ec)
1689 "asio open handle_close_handshake_timeout error: "+ec.message());
1697template <
typename config>
1698void connection<config>::terminate(lib::error_code
const & ec) {
1704 if (m_handshake_timer) {
1705 m_handshake_timer->cancel();
1706 m_handshake_timer.reset();
1709 terminate_status tstat = unknown;
1713 m_local_close_reason = ec.message();
1718 m_http_state = session::http_state::closed;
1720 if (m_state == session::state::connecting) {
1721 m_state = session::state::closed;
1729 }
else if (m_state != session::state::closed) {
1730 m_state = session::state::closed;
1734 "terminate called on connection that was already terminated");
1740 transport_con_type::async_shutdown(
1742 &type::handle_terminate,
1745 lib::placeholders::_1
1750template <
typename config>
1751void connection<config>::handle_terminate(terminate_status tstat,
1752 lib::error_code
const & ec)
1764 if (tstat == failed) {
1766 if (m_fail_handler) {
1767 m_fail_handler(m_connection_hdl);
1770 }
else if (tstat == closed) {
1771 if (m_close_handler) {
1772 m_close_handler(m_connection_hdl);
1782 if (m_termination_handler) {
1784 m_termination_handler(type::get_shared());
1785 }
catch (std::exception
const & e) {
1787 std::string(
"termination_handler call failed. Reason was: ")+e.what());
1792template <
typename config>
1797 scoped_lock_type lock(m_write_lock);
1809 message_ptr next_message = write_pop();
1810 while (next_message) {
1811 m_current_msgs.push_back(next_message);
1812 if (!next_message->get_terminal()) {
1813 next_message = write_pop();
1815 next_message = message_ptr();
1819 if (m_current_msgs.empty()) {
1826 m_write_flag =
true;
1830 typename std::vector<message_ptr>::iterator it;
1831 for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1832 std::string
const & header = (*it)->get_header();
1833 std::string
const & payload = (*it)->get_payload();
1842 std::stringstream general,header,payload;
1844 general <<
"Dispatching write containing " << m_current_msgs.size()
1845 <<
" message(s) containing ";
1846 header <<
"Header Bytes: \n";
1847 payload <<
"Payload Bytes: \n";
1852 for (
size_t i = 0; i < m_current_msgs.size(); i++) {
1853 hbytes += m_current_msgs[i]->get_header().size();
1854 pbytes += m_current_msgs[i]->get_payload().size();
1857 header <<
"[" << i <<
"] ("
1858 << m_current_msgs[i]->get_header().size() <<
") "
1863 payload <<
"[" << i <<
"] ("
1864 << m_current_msgs[i]->get_payload().size() <<
") ["<<m_current_msgs[i]->get_opcode()<<
"] "
1865 << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1866 m_current_msgs[i]->get_payload() :
1874 general << hbytes <<
" header bytes and " << pbytes <<
" payload bytes";
1882 transport_con_type::async_write(
1884 m_write_frame_handler
1888template <
typename config>
1895 bool terminal = m_current_msgs.back()->get_terminal();
1897 m_send_buffer.clear();
1898 m_current_msgs.clear();
1903 this->terminate(ec);
1908 this->terminate(lib::error_code());
1912 bool needs_writing =
false;
1914 scoped_lock_type lock(m_write_lock);
1917 m_write_flag =
false;
1919 needs_writing = !m_send_queue.empty();
1922 if (needs_writing) {
1923 transport_con_type::dispatch(lib::bind(
1930template <
typename config>
1936template <
typename config>
1941 frame::opcode::value op = msg->get_opcode();
1944 std::stringstream s;
1945 s <<
"Control frame received with opcode " << op;
1948 if (m_state == session::state::closed) {
1952 if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1957 if (op == frame::opcode::PING) {
1958 bool should_reply =
true;
1960 if (m_ping_handler) {
1961 should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1965 this->pong(msg->get_payload(),ec);
1970 }
else if (op == frame::opcode::PONG) {
1971 if (m_pong_handler) {
1972 m_pong_handler(m_connection_hdl, msg->get_payload());
1975 m_ping_timer->cancel();
1977 }
else if (op == frame::opcode::CLOSE) {
1984 if (config::drop_on_protocol_error) {
1985 s <<
"Received invalid close code " << m_remote_close_code
1986 <<
" dropping connection per config.";
1988 this->terminate(ec);
1990 s <<
"Received invalid close code " << m_remote_close_code
1991 <<
" sending acknowledgement and closing";
1994 "Invalid close code");
2004 if (config::drop_on_protocol_error) {
2006 "Received invalid close reason. Dropping connection per config");
2007 this->terminate(ec);
2010 "Received invalid close reason. Sending acknowledgement and closing");
2012 "Invalid close reason");
2020 if (m_state == session::state::open) {
2022 s <<
"Received close frame with code " << m_remote_close_code
2023 <<
" and reason " << m_remote_close_reason;
2026 ec = send_close_ack();
2030 }
else if (m_state == session::state::closing && !m_was_clean) {
2044 terminate(lib::error_code());
2057template <
typename config>
2059 std::string
const & reason)
2061 return send_close_frame(code,reason,
true,m_is_server);
2064template <
typename config>
2066 std::string
const & reason,
bool ack,
bool terminal)
2078 if (config::silent_close) {
2081 m_local_close_reason.clear();
2084 m_local_close_code = code;
2085 m_local_close_reason = reason;
2089 m_local_close_reason.clear();
2092 "acknowledging a no-status close with normal code");
2094 m_local_close_reason.clear();
2097 m_local_close_code = m_remote_close_code;
2098 m_local_close_reason = m_remote_close_reason;
2101 std::stringstream s;
2102 s <<
"Closing with code: " << m_local_close_code <<
", and reason: "
2103 << m_local_close_reason;
2106 message_ptr msg = m_msg_manager->get_message();
2111 lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2112 m_local_close_reason,msg);
2121 msg->set_terminal(
true);
2124 m_state = session::state::closing;
2132 if (m_close_handshake_timeout_dur > 0) {
2133 m_handshake_timer = transport_con_type::set_timer(
2134 m_close_handshake_timeout_dur,
2136 &type::handle_close_handshake_timeout,
2138 lib::placeholders::_1
2143 bool needs_writing =
false;
2145 scoped_lock_type lock(m_write_lock);
2147 needs_writing = !m_write_flag && !m_send_queue.empty();
2150 if (needs_writing) {
2151 transport_con_type::dispatch(lib::bind(
2157 return lib::error_code();
2160template <
typename config>
2161typename connection<config>::processor_ptr
2162connection<config>::get_processor(
int version)
const {
2169 p = lib::make_shared<processor::hybi00<config> >(
2170 transport_con_type::is_secure(),
2176 p = lib::make_shared<processor::hybi07<config> >(
2177 transport_con_type::is_secure(),
2184 p = lib::make_shared<processor::hybi08<config> >(
2185 transport_con_type::is_secure(),
2192 p = lib::make_shared<processor::hybi13<config> >(
2193 transport_con_type::is_secure(),
2204 p->set_max_message_size(m_max_message_size);
2209template <
typename config>
2210void connection<config>::write_push(
typename config::message_type::ptr msg)
2216 m_send_buffer_size += msg->get_payload().size();
2217 m_send_queue.push(msg);
2220 std::stringstream s;
2221 s <<
"write_push: message count: " << m_send_queue.size()
2222 <<
" buffer size: " << m_send_buffer_size;
2227template <
typename config>
2228typename config::message_type::ptr connection<config>::write_pop()
2232 if (m_send_queue.empty()) {
2236 msg = m_send_queue.front();
2238 m_send_buffer_size -= msg->get_payload().size();
2242 std::stringstream s;
2243 s <<
"write_pop: message count: " << m_send_queue.size()
2244 <<
" buffer size: " << m_send_buffer_size;
2250template <
typename config>
2251void connection<config>::log_open_result()
2253 std::stringstream s;
2263 s << (version == -1 ?
"HTTP" :
"WebSocket") <<
" Connection ";
2266 s << transport_con_type::get_remote_endpoint() <<
" ";
2269 if (version != -1) {
2270 s <<
"v" << version <<
" ";
2274 std::string ua = m_request.get_header(
"User-Agent");
2283 s << (m_uri ? m_uri->get_resource() :
"NULL") <<
" ";
2286 s << m_response.get_status_code();
2291template <
typename config>
2292void connection<config>::log_close_result()
2294 std::stringstream s;
2297 <<
"close local:[" << m_local_close_code
2298 << (m_local_close_reason.empty() ?
"" :
","+m_local_close_reason)
2299 <<
"] remote:[" << m_remote_close_code
2300 << (m_remote_close_reason.empty() ?
"" :
","+m_remote_close_reason) <<
"]";
2305template <
typename config>
2306void connection<config>::log_fail_result()
2308 std::stringstream s;
2313 s <<
"WebSocket Connection ";
2316 s << transport_con_type::get_remote_endpoint();
2320 s <<
" v" << version;
2324 std::string ua = m_request.get_header(
"User-Agent");
2333 s << (m_uri ? m_uri->get_resource() :
"-");
2336 s <<
" " << m_response.get_status_code();
2339 s <<
" " << m_ec <<
" " << m_ec.message();
2344template <
typename config>
2345void connection<config>::log_http_result() {
2346 std::stringstream s;
2354 s << (m_request.get_header(
"host").empty() ?
"-" : m_request.get_header(
"host"))
2355 <<
" " << transport_con_type::get_remote_endpoint()
2356 <<
" \"" << m_request.get_method()
2357 <<
" " << (m_uri ? m_uri->get_resource() :
"-")
2358 <<
" " << m_request.get_version() <<
"\" " << m_response.get_status_code()
2359 <<
" " << m_response.get_body().size();
2362 std::string ua = m_request.get_header(
"User-Agent");
Represents an individual WebSocket connection.
void handle_interrupt()
Transport inturrupt callback.
lib::error_code interrupt()
Asyncronously invoke handler::on_inturrupt.
void start()
Start the connection state machine.
std::string const & get_request_body() const
Retrieve a request body.
void ping(std::string const &payload)
Send a ping.
lib::error_code defer_http_response()
Defer HTTP Response until later (Exception free)
lib::error_code resume_reading()
Resume reading of new data.
void add_subprotocol(std::string const &request, lib::error_code &ec)
Adds the given subprotocol string to the request list (exception free)
bool get_secure() const
Returns the secure flag from the connection URI.
void set_body(std::string const &value)
Set response body content.
size_t get_buffered_amount() const
Get the size of the outgoing write buffer (in payload bytes)
std::string const & get_origin() const
Return the same origin policy origin value from the opening request.
std::string const & get_host() const
Returns the host component of the connection URI.
std::string const & get_resource() const
Returns the resource component of the connection URI.
void select_subprotocol(std::string const &value, lib::error_code &ec)
Select a subprotocol to use (exception free)
std::string const & get_request_header(std::string const &key) const
Retrieve a request header.
lib::error_code process_handshake_request()
void handle_pause_reading()
Pause reading callback.
std::vector< int > const & get_supported_versions() const
Get array of WebSocket protocol versions that this connection supports.
void remove_header(std::string const &key)
Remove a header.
uri_ptr get_uri() const
Gets the connection URI.
std::string const & get_response_header(std::string const &key) const
Retrieve a response header.
void handle_write_frame(lib::error_code const &ec)
Process the results of a frame write operation and start the next write.
void write_frame()
Checks if there are frames in the send queue and if there are sends one.
session::state::value get_state() const
Return the connection state.
void set_status(http::status_code::value code)
Set response status code and message.
void replace_header(std::string const &key, std::string const &val)
Replace a header.
void pong(std::string const &payload)
Send a pong.
void send_http_response()
Send deferred HTTP Response.
lib::error_code initialize_processor()
void set_uri(uri_ptr uri)
Sets the connection URI.
void set_termination_handler(termination_handler new_handler)
lib::error_code pause_reading()
Pause reading of new data.
void handle_resume_reading()
Resume reading callback.
std::vector< std::string > const & get_requested_subprotocols() const
Gets all of the subprotocols requested by the client.
void close(close::status::value const code, std::string const &reason)
Close the connection.
std::string const & get_subprotocol() const
Gets the negotated subprotocol.
void handle_pong_timeout(std::string payload, lib::error_code const &ec)
Utility method that gets called back when the ping timer expires.
lib::error_code send(std::string const &payload, frame::opcode::value op=frame::opcode::text)
Create a message and then add it to the outgoing send queue.
void read_frame()
Issue a new transport read unless reading is paused.
uint16_t get_port() const
Returns the port component of the connection URI.
void append_header(std::string const &key, std::string const &val)
Append a header.
bool terminal(value code)
Determine if the code represents an unrecoverable error.
static value const protocol_error
A protocol error occurred.
static value const no_status
A dummy value to indicate that no status code was received.
static value const normal
static value const blank
A blank value for internal use.
static value const abnormal_close
A dummy value to indicate that the connection was closed abnormally.
uint16_t value
The type of a close code value.
std::string extract_reason(std::string const &payload, lib::error_code &ec)
Extract the reason string from a close payload.
status::value extract_code(std::string const &payload, lib::error_code &ec)
Extract a close code value from a close payload.
@ general
Catch-all library error.
@ unrequested_subprotocol
Selected subprotocol was not requested by the client.
@ client_only
Attempted to use a client specific feature on a server endpoint.
@ http_connection_ended
HTTP connection ended.
@ no_outgoing_buffers
The endpoint is out of outgoing message buffers.
@ http_parse_error
HTTP parse error.
@ invalid_state
The connection was in the wrong state for this operation.
@ extension_neg_failed
Extension negotiation failed.
@ rejected
Connection rejected.
@ unsupported_version
Unsupported WebSocket protocol version.
@ server_only
Attempted to use a server specific feature on a client endpoint.
@ close_handshake_timeout
WebSocket close handshake timed out.
@ invalid_subprotocol
Invalid subprotocol.
@ open_handshake_timeout
WebSocket opening handshake timed out.
@ invalid_version
Invalid WebSocket protocol version.
@ invalid_uri
An invalid uri was supplied.
static uint8_t const close_reason_size
Maximum size of close frame reason.
bool is_control(value v)
Check if an opcode is for a control frame.
bool is_not_token_char(unsigned char c)
Is the character a non-token.
close::status::value to_ws(lib::error_code ec)
Converts a processor error_code into a websocket close code.
@ extension_parse_error
Error parsing extensions.
lib::error_code make_error_code(error::processor_errors e)
Create an error code with the given value and the processor category.
int get_websocket_version(request_type &r)
Extract the version from a WebSocket handshake request.
uri_ptr get_uri_from_host(request_type &request, std::string scheme)
Extract a URI ptr from the host header of the request.
bool is_websocket_handshake(request_type &r)
Determine whether or not a generic HTTP request is a WebSocket handshake.
@ operation_aborted
Operation aborted.
@ action_after_shutdown
read or write after shutdown
std::string to_hex(std::string const &input)
Convert std::string to ascii printed string of hex digits.
std::string string_replace_all(std::string subject, std::string const &search, std::string const &replace)
Replace all occurrances of a substring with another.
Namespace for the WebSocket++ project.
lib::shared_ptr< uri > uri_ptr
Pointer to a URI.
static std::vector< int > const versions_supported(helper, helper+4)
Container that stores the list of protocol versions supported.
static level const fail
One line for each failed WebSocket connection with details.
static level const devel
Development messages (warning: very chatty)
static level const frame_payload
One line per frame, includes the full message payload (warning: chatty)
static level const connect
Information about new connections.
static level const frame_header
One line per frame, includes the full frame header.
static level const control
One line per control frame.
static level const disconnect
One line for each closed connection. Includes closing codes and reasons.
static level const http
Access related to HTTP requests.
static level const devel
Low level debugging information (warning: very chatty)
static level const rerror
A simple utility buffer class.