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>
50 namespace istate = session::internal_state;
52 template <
typename config>
54 termination_handler new_handler)
57 "connection set_termination_handler");
61 m_termination_handler = new_handler;
64 template <
typename config>
67 return m_processor->get_origin(m_request);
70 template <
typename config>
73 return m_send_buffer_size;
76 template <
typename config>
82 template <
typename config>
84 frame::opcode::value op)
86 message_ptr msg = m_msg_manager->get_message(op,payload.size());
87 msg->append_payload(payload);
92 template <
typename config>
94 frame::opcode::value op)
96 message_ptr msg = m_msg_manager->get_message(op,len);
97 msg->append_payload(payload,len);
102 template <
typename config>
110 scoped_lock_type lock(m_connection_state_lock);
111 if (m_state != session::state::open) {
116 message_ptr outgoing_msg;
117 bool needs_writing =
false;
119 if (msg->get_prepared()) {
122 scoped_lock_type lock(m_write_lock);
123 write_push(outgoing_msg);
124 needs_writing = !m_write_flag && !m_send_queue.empty();
126 outgoing_msg = m_msg_manager->get_message();
132 scoped_lock_type lock(m_write_lock);
133 lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg);
139 write_push(outgoing_msg);
140 needs_writing = !m_write_flag && !m_send_queue.empty();
144 transport_con_type::dispatch(lib::bind(
150 return lib::error_code();
153 template <
typename config>
160 scoped_lock_type lock(m_connection_state_lock);
161 if (m_state != session::state::open) {
162 std::stringstream ss;
163 ss <<
"connection::ping called from invalid state " << m_state;
170 message_ptr msg = m_msg_manager->get_message();
176 ec = m_processor->prepare_ping(payload,msg);
180 if (m_pong_timeout_handler) {
183 m_ping_timer->cancel();
186 if (m_pong_timeout_dur > 0) {
187 m_ping_timer = transport_con_type::set_timer(
190 &type::handle_pong_timeout,
193 lib::placeholders::_1
201 set but the transport in use does not support timeouts.");
205 bool needs_writing =
false;
207 scoped_lock_type lock(m_write_lock);
209 needs_writing = !m_write_flag && !m_send_queue.empty();
213 transport_con_type::dispatch(lib::bind(
219 ec = lib::error_code();
222 template<
typename config>
231 template<
typename config>
233 lib::error_code
const & ec)
245 if (m_pong_timeout_handler) {
246 m_pong_timeout_handler(m_connection_hdl,payload);
250 template <
typename config>
257 scoped_lock_type lock(m_connection_state_lock);
258 if (m_state != session::state::open) {
259 std::stringstream ss;
260 ss <<
"connection::pong called from invalid state " << m_state;
267 message_ptr msg = m_msg_manager->get_message();
273 ec = m_processor->prepare_pong(payload,msg);
276 bool needs_writing =
false;
278 scoped_lock_type lock(m_write_lock);
280 needs_writing = !m_write_flag && !m_send_queue.empty();
284 transport_con_type::dispatch(lib::bind(
290 ec = lib::error_code();
293 template<
typename config>
302 template <
typename config>
304 std::string
const & reason, lib::error_code & ec)
311 std::string tr(reason,0,std::min<size_t>(reason.size(),
314 scoped_lock_type lock(m_connection_state_lock);
316 if (m_state != session::state::open) {
324 template<
typename config>
326 std::string
const & reason)
329 close(code,reason,ec);
339 template <
typename config>
342 return transport_con_type::interrupt(
344 &type::handle_interrupt,
351 template <
typename config>
353 if (m_interrupt_handler) {
354 m_interrupt_handler(m_connection_hdl);
358 template <
typename config>
361 return transport_con_type::dispatch(
363 &type::handle_pause_reading,
370 template <
typename config>
376 template <
typename config>
379 return transport_con_type::dispatch(
381 &type::handle_resume_reading,
388 template <
typename config>
404 template <
typename config>
407 return m_uri->get_secure();
410 template <
typename config>
413 return m_uri->get_host();
416 template <
typename config>
419 return m_uri->get_resource();
422 template <
typename config>
425 return m_uri->get_port();
428 template <
typename config>
434 template <
typename config>
445 template <
typename config>
447 return m_subprotocol;
450 template <
typename config>
451 std::vector<std::string>
const &
453 return m_requested_subprotocols;
456 template <
typename config>
458 lib::error_code & ec)
466 if (value.empty() || std::find_if(value.begin(),value.end(),
473 m_requested_subprotocols.push_back(value);
476 template <
typename config>
479 this->add_subprotocol(value,ec);
486 template <
typename config>
488 lib::error_code & ec)
496 ec = lib::error_code();
500 std::vector<std::string>::iterator it;
502 it = std::find(m_requested_subprotocols.begin(),
503 m_requested_subprotocols.end(),
506 if (it == m_requested_subprotocols.end()) {
511 m_subprotocol =
value;
514 template <
typename config>
517 this->select_subprotocol(value,ec);
524 template <
typename config>
527 return m_request.get_header(key);
530 template <
typename config>
533 return m_request.get_body();
536 template <
typename config>
539 return m_response.get_header(key);
542 template <
typename config>
545 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
546 throw exception(
"Call to set_status from invalid state",
549 m_response.set_status(code);
551 template <
typename config>
553 std::string
const & msg)
555 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
556 throw exception(
"Call to set_status from invalid state",
560 m_response.set_status(code,msg);
562 template <
typename config>
564 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
565 throw exception(
"Call to set_status from invalid state",
569 m_response.set_body(value);
572 template <
typename config>
574 std::string
const & val)
577 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
579 m_response.append_header(key,val);
581 throw exception(
"Call to append_header from invalid state",
585 if (m_internal_state == istate::USER_INIT) {
587 m_request.append_header(key,val);
589 throw exception(
"Call to append_header from invalid state",
594 template <
typename config>
596 std::string
const & val)
599 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
601 m_response.replace_header(key,val);
603 throw exception(
"Call to replace_header from invalid state",
607 if (m_internal_state == istate::USER_INIT) {
609 m_request.replace_header(key,val);
611 throw exception(
"Call to replace_header from invalid state",
616 template <
typename config>
620 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
622 m_response.remove_header(key);
624 throw exception(
"Call to remove_header from invalid state",
628 if (m_internal_state == istate::USER_INIT) {
630 m_request.remove_header(key);
632 throw exception(
"Call to remove_header from invalid state",
645 template <
typename config>
649 if (m_internal_state != istate::USER_INIT) {
655 m_internal_state = istate::TRANSPORT_INIT;
660 transport_con_type::init(
662 &type::handle_transport_init,
664 lib::placeholders::_1
669 template <
typename config>
670 void connection<config>::handle_transport_init(lib::error_code
const & ec) {
673 lib::error_code ecm = ec;
675 if (m_internal_state != istate::TRANSPORT_INIT) {
677 "handle_transport_init must be called from transport init state");
683 s <<
"handle_transport_init received error: "<< ecm.message();
686 this->terminate(ecm);
692 m_internal_state = istate::READ_HTTP_REQUEST;
693 this->read_handshake(1);
697 m_internal_state = istate::WRITE_HTTP_REQUEST;
698 m_processor = get_processor(config::client_version);
699 this->send_http_request();
703 template <
typename config>
704 void connection<config>::read_handshake(
size_t num_bytes) {
707 if (m_open_handshake_timeout_dur > 0) {
708 m_handshake_timer = transport_con_type::set_timer(
709 m_open_handshake_timeout_dur,
711 &type::handle_open_handshake_timeout,
713 lib::placeholders::_1
718 transport_con_type::async_read_at_least(
721 config::connection_read_buffer_size,
723 &type::handle_read_handshake,
725 lib::placeholders::_1,
726 lib::placeholders::_2
733 template <
typename config>
734 void connection<config>::handle_read_handshake(lib::error_code
const & ec,
735 size_t bytes_transferred)
739 lib::error_code ecm = ec;
742 scoped_lock_type lock(m_connection_state_lock);
744 if (m_state == session::state::connecting) {
745 if (m_internal_state != istate::READ_HTTP_REQUEST) {
748 }
else if (m_state == session::state::closed) {
753 "handle_read_handshake invoked after connection was closed");
764 "got (expected) eof/state error from closed con");
769 this->terminate(ecm);
774 if (bytes_transferred > config::connection_read_buffer_size) {
780 size_t bytes_processed = 0;
782 bytes_processed = m_request.consume(m_buf,bytes_transferred);
783 }
catch (http::exception &e) {
786 m_response.set_status(e.m_error_code,e.m_error_msg);
793 if (bytes_processed > bytes_transferred) {
801 s <<
"bytes_transferred: " << bytes_transferred
802 <<
" bytes, bytes processed: " << bytes_processed <<
" bytes";
806 if (m_request.ready()) {
807 lib::error_code processor_ec = this->initialize_processor();
809 this->send_http_response_error(processor_ec);
813 if (m_processor && m_processor->get_version() == 0) {
816 if (bytes_transferred-bytes_processed >= 8) {
817 m_request.replace_header(
818 "Sec-WebSocket-Key3",
819 std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
821 bytes_processed += 8;
825 m_response.set_status(http::status_code::internal_server_error);
833 if (m_request.get_header(
"Sec-WebSocket-Key3") !=
"") {
842 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
843 m_buf_cursor = bytes_transferred-bytes_processed;
846 m_internal_state = istate::PROCESS_HTTP_REQUEST;
849 lib::error_code handshake_ec = this->process_handshake_request();
850 this->send_http_response(handshake_ec);
853 transport_con_type::async_read_at_least(
856 config::connection_read_buffer_size,
858 &type::handle_read_handshake,
860 lib::placeholders::_1,
861 lib::placeholders::_2
872 template <
typename config>
873 void connection<config>::send_http_response_error(lib::error_code
const & ec) {
874 if (m_internal_state != istate::READ_HTTP_REQUEST) {
876 "send_http_response_error called in invalid state");
881 m_internal_state = istate::PROCESS_HTTP_REQUEST;
883 this->send_http_response(ec);
888 template <
typename config>
889 void connection<config>::handle_read_frame(lib::error_code
const & ec,
890 size_t bytes_transferred)
894 lib::error_code ecm = ec;
896 if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
904 if (m_state == session::state::closed) {
909 }
else if (m_state == session::state::closing && !m_is_server) {
913 terminate(lib::error_code());
921 if (m_state == session::state::closed) {
923 "handle_read_frame: got invalid istate in closed state");
927 if (m_state == session::state::closed) {
931 terminate(lib::error_code());
939 log_err(echannel,
"handle_read_frame", ecm);
940 this->terminate(ecm);
955 s <<
"p = " << p <<
" bytes transferred = " << bytes_transferred;
959 while (p < bytes_transferred) {
962 s <<
"calling consume with " << bytes_transferred-p <<
" bytes";
966 lib::error_code consume_ec;
968 p += m_processor->consume(
969 reinterpret_cast<uint8_t*>(m_buf)+p,
976 s <<
"bytes left after consume: " << bytes_transferred-p;
982 if (config::drop_on_protocol_error) {
983 this->terminate(consume_ec);
986 lib::error_code close_ec;
989 consume_ec.message(),
995 this->terminate(close_ec);
1002 if (m_processor->ready()) {
1004 std::stringstream s;
1005 s <<
"Complete message received. Dispatching";
1009 message_ptr msg = m_processor->get_message();
1015 if (m_state != session::state::open) {
1017 }
else if (m_message_handler) {
1018 m_message_handler(m_connection_hdl, msg);
1021 process_control_frame(msg);
1030 template <
typename config>
1036 transport_con_type::async_read_at_least(
1046 config::connection_read_buffer_size,
1051 template <
typename config>
1057 return lib::error_code();
1064 m_response.set_status(http::status_code::bad_request);
1068 m_processor = get_processor(version);
1072 return lib::error_code();
1078 m_response.set_status(http::status_code::bad_request);
1080 std::stringstream ss;
1081 std::string sep =
"";
1082 std::vector<int>::const_iterator it;
1089 m_response.replace_header(
"Sec-WebSocket-Version",ss.str());
1093 template <
typename config>
1104 (transport_con_type::is_secure() ?
"https" :
"http")
1107 if (!m_uri->get_valid()) {
1109 m_response.set_status(http::status_code::bad_request);
1113 if (m_http_handler) {
1115 m_http_handler(m_connection_hdl);
1116 if (m_state == session::state::closed) {
1120 set_status(http::status_code::upgrade_required);
1124 return lib::error_code();
1127 lib::error_code ec = m_processor->validate_handshake(m_request);
1133 m_response.set_status(http::status_code::bad_request);
1139 std::pair<lib::error_code,std::string> neg_results;
1140 neg_results = m_processor->negotiate_extensions(m_request);
1142 if (neg_results.first) {
1146 m_response.set_status(http::status_code::bad_request);
1147 return neg_results.first;
1152 if (neg_results.second.size() > 0) {
1153 m_response.replace_header(
"Sec-WebSocket-Extensions",
1154 neg_results.second);
1159 m_uri = m_processor->get_uri(m_request);
1162 if (!m_uri->get_valid()) {
1164 m_response.set_status(http::status_code::bad_request);
1169 lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1170 m_requested_subprotocols);
1177 if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1178 m_response.set_status(http::status_code::switching_protocols);
1182 ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1185 std::stringstream s;
1186 s <<
"Processing error: " << ec <<
"(" << ec.message() <<
")";
1189 m_response.set_status(http::status_code::internal_server_error);
1199 if (m_response.get_status_code() == http::status_code::uninitialized) {
1200 m_response.set_status(http::status_code::bad_request);
1206 return lib::error_code();
1209 template <
typename config>
1218 if (m_response.get_status_code() == http::status_code::uninitialized) {
1219 m_response.
set_status(http::status_code::internal_server_error);
1225 m_response.set_version(
"HTTP/1.1");
1228 if (m_response.get_header(
"Server") ==
"") {
1229 if (!m_user_agent.empty()) {
1230 m_response.replace_header(
"Server",m_user_agent);
1232 m_response.remove_header(
"Server");
1238 m_handshake_buffer = m_processor->get_raw(m_response);
1241 m_handshake_buffer = m_response.raw();
1246 if (m_response.get_header(
"Sec-WebSocket-Key3") !=
"") {
1253 transport_con_type::async_write(
1254 m_handshake_buffer.data(),
1255 m_handshake_buffer.size(),
1257 &type::handle_send_http_response,
1259 lib::placeholders::_1
1264 template <
typename config>
1265 void connection<config>::handle_send_http_response(lib::error_code
const & ec) {
1268 lib::error_code ecm = ec;
1271 scoped_lock_type lock(m_connection_state_lock);
1273 if (m_state == session::state::connecting) {
1274 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1277 }
else if (m_state == session::state::closed) {
1282 "handle_send_http_response invoked after connection was closed");
1293 "got (expected) eof/state error from closed con");
1298 this->terminate(ecm);
1302 if (m_handshake_timer) {
1303 m_handshake_timer->cancel();
1304 m_handshake_timer.reset();
1307 if (m_response.get_status_code() != http::status_code::switching_protocols)
1314 std::stringstream s;
1315 s <<
"Handshake ended with HTTP error: "
1316 << m_response.get_status_code();
1322 this->log_http_result();
1326 "got to writing HTTP results with m_ec set: "+m_ec.message());
1331 this->terminate(m_ec);
1335 this->log_open_result();
1337 m_internal_state = istate::PROCESS_CONNECTION;
1338 m_state = session::state::open;
1340 if (m_open_handler) {
1341 m_open_handler(m_connection_hdl);
1344 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1347 template <
typename config>
1348 void connection<config>::send_http_request() {
1357 ec = m_processor->client_handshake_request(m_request,m_uri,
1358 m_requested_subprotocols);
1370 if (m_request.get_header(
"User-Agent") ==
"") {
1371 if (!m_user_agent.empty()) {
1372 m_request.replace_header(
"User-Agent",m_user_agent);
1374 m_request.remove_header(
"User-Agent");
1378 m_handshake_buffer = m_request.raw();
1384 if (m_open_handshake_timeout_dur > 0) {
1385 m_handshake_timer = transport_con_type::set_timer(
1386 m_open_handshake_timeout_dur,
1388 &type::handle_open_handshake_timeout,
1390 lib::placeholders::_1
1395 transport_con_type::async_write(
1396 m_handshake_buffer.data(),
1397 m_handshake_buffer.size(),
1399 &type::handle_send_http_request,
1401 lib::placeholders::_1
1406 template <
typename config>
1407 void connection<config>::handle_send_http_request(lib::error_code
const & ec) {
1410 lib::error_code ecm = ec;
1413 scoped_lock_type lock(m_connection_state_lock);
1415 if (m_state == session::state::connecting) {
1416 if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1419 m_internal_state = istate::READ_HTTP_RESPONSE;
1421 }
else if (m_state == session::state::closed) {
1426 "handle_send_http_request invoked after connection was closed");
1437 "got (expected) eof/state error from closed con");
1442 this->terminate(ecm);
1446 transport_con_type::async_read_at_least(
1449 config::connection_read_buffer_size,
1451 &type::handle_read_http_response,
1453 lib::placeholders::_1,
1454 lib::placeholders::_2
1459 template <
typename config>
1460 void connection<config>::handle_read_http_response(lib::error_code
const & ec,
1461 size_t bytes_transferred)
1465 lib::error_code ecm = ec;
1468 scoped_lock_type lock(m_connection_state_lock);
1470 if (m_state == session::state::connecting) {
1471 if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1474 }
else if (m_state == session::state::closed) {
1479 "handle_read_http_response invoked after connection was closed");
1490 "got (expected) eof/state error from closed con");
1495 this->terminate(ecm);
1499 size_t bytes_processed = 0;
1502 bytes_processed = m_response.consume(m_buf,bytes_transferred);
1503 }
catch (http::exception & e) {
1505 std::string(
"error in handle_read_http_response: ")+e.what());
1512 if (m_response.headers_ready()) {
1513 if (m_handshake_timer) {
1514 m_handshake_timer->cancel();
1515 m_handshake_timer.reset();
1518 lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1524 this->terminate(validate_ec);
1529 m_internal_state = istate::PROCESS_CONNECTION;
1530 m_state = session::state::open;
1532 this->log_open_result();
1534 if (m_open_handler) {
1535 m_open_handler(m_connection_hdl);
1541 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1542 m_buf_cursor = bytes_transferred-bytes_processed;
1544 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1546 transport_con_type::async_read_at_least(
1549 config::connection_read_buffer_size,
1551 &type::handle_read_http_response,
1553 lib::placeholders::_1,
1554 lib::placeholders::_2
1560 template <
typename config>
1561 void connection<config>::handle_open_handshake_timeout(
1562 lib::error_code
const & ec)
1568 "open handle_open_handshake_timeout error: "+ec.message());
1576 template <
typename config>
1577 void connection<config>::handle_close_handshake_timeout(
1578 lib::error_code
const & ec)
1584 "asio open handle_close_handshake_timeout error: "+ec.message());
1592 template <
typename config>
1593 void connection<config>::terminate(lib::error_code
const & ec) {
1599 if (m_handshake_timer) {
1600 m_handshake_timer->cancel();
1601 m_handshake_timer.reset();
1604 terminate_status tstat = unknown;
1608 m_local_close_reason = ec.message();
1612 if (m_state == session::state::connecting) {
1613 m_state = session::state::closed;
1621 }
else if (m_state != session::state::closed) {
1622 m_state = session::state::closed;
1626 "terminate called on connection that was already terminated");
1632 transport_con_type::async_shutdown(
1634 &type::handle_terminate,
1637 lib::placeholders::_1
1642 template <
typename config>
1643 void connection<config>::handle_terminate(terminate_status tstat,
1644 lib::error_code
const & ec)
1656 if (tstat == failed) {
1658 if (m_fail_handler) {
1659 m_fail_handler(m_connection_hdl);
1662 }
else if (tstat == closed) {
1663 if (m_close_handler) {
1664 m_close_handler(m_connection_hdl);
1674 if (m_termination_handler) {
1676 m_termination_handler(type::get_shared());
1677 }
catch (std::exception
const & e) {
1679 std::string(
"termination_handler call failed. Reason was: ")+e.what());
1684 template <
typename config>
1689 scoped_lock_type lock(m_write_lock);
1701 message_ptr next_message = write_pop();
1702 while (next_message) {
1703 m_current_msgs.push_back(next_message);
1704 if (!next_message->get_terminal()) {
1705 next_message = write_pop();
1707 next_message = message_ptr();
1711 if (m_current_msgs.empty()) {
1718 m_write_flag =
true;
1722 typename std::vector<message_ptr>::iterator it;
1723 for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1724 std::string
const & header = (*it)->get_header();
1725 std::string
const & payload = (*it)->get_payload();
1734 std::stringstream general,header,payload;
1736 general <<
"Dispatching write containing " << m_current_msgs.size()
1737 <<
" message(s) containing ";
1738 header <<
"Header Bytes: \n";
1739 payload <<
"Payload Bytes: \n";
1744 for (
size_t i = 0; i < m_current_msgs.size(); i++) {
1745 hbytes += m_current_msgs[i]->get_header().size();
1746 pbytes += m_current_msgs[i]->get_payload().size();
1749 header <<
"[" << i <<
"] ("
1750 << m_current_msgs[i]->get_header().size() <<
") "
1755 payload <<
"[" << i <<
"] ("
1756 << m_current_msgs[i]->get_payload().size() <<
") ["<<m_current_msgs[i]->get_opcode()<<
"] "
1757 << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1758 m_current_msgs[i]->get_payload() :
1766 general << hbytes <<
" header bytes and " << pbytes <<
" payload bytes";
1774 transport_con_type::async_write(
1776 m_write_frame_handler
1780 template <
typename config>
1787 bool terminal = m_current_msgs.back()->get_terminal();
1789 m_send_buffer.clear();
1790 m_current_msgs.clear();
1795 this->terminate(ec);
1800 this->terminate(lib::error_code());
1804 bool needs_writing =
false;
1806 scoped_lock_type lock(m_write_lock);
1809 m_write_flag =
false;
1811 needs_writing = !m_send_queue.empty();
1814 if (needs_writing) {
1815 transport_con_type::dispatch(lib::bind(
1822 template <
typename config>
1828 template <
typename config>
1833 frame::opcode::value op = msg->get_opcode();
1836 std::stringstream s;
1837 s <<
"Control frame received with opcode " << op;
1840 if (m_state == session::state::closed) {
1844 if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1849 if (op == frame::opcode::PING) {
1850 bool should_reply =
true;
1852 if (m_ping_handler) {
1853 should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1857 this->pong(msg->get_payload(),ec);
1862 }
else if (op == frame::opcode::PONG) {
1863 if (m_pong_handler) {
1864 m_pong_handler(m_connection_hdl, msg->get_payload());
1867 m_ping_timer->cancel();
1869 }
else if (op == frame::opcode::CLOSE) {
1876 if (config::drop_on_protocol_error) {
1877 s <<
"Received invalid close code " << m_remote_close_code
1878 <<
" dropping connection per config.";
1880 this->terminate(ec);
1882 s <<
"Received invalid close code " << m_remote_close_code
1883 <<
" sending acknowledgement and closing";
1886 "Invalid close code");
1896 if (config::drop_on_protocol_error) {
1898 "Received invalid close reason. Dropping connection per config");
1899 this->terminate(ec);
1902 "Received invalid close reason. Sending acknowledgement and closing");
1904 "Invalid close reason");
1912 if (m_state == session::state::open) {
1914 s <<
"Received close frame with code " << m_remote_close_code
1915 <<
" and reason " << m_remote_close_reason;
1918 ec = send_close_ack();
1922 }
else if (m_state == session::state::closing && !m_was_clean) {
1936 terminate(lib::error_code());
1949 template <
typename config>
1951 std::string
const & reason)
1953 return send_close_frame(code,reason,
true,m_is_server);
1956 template <
typename config>
1958 std::string
const & reason,
bool ack,
bool terminal)
1970 if (config::silent_close) {
1973 m_local_close_reason =
"";
1976 m_local_close_code = code;
1977 m_local_close_reason = reason;
1981 m_local_close_reason =
"";
1984 "acknowledging a no-status close with normal code");
1986 m_local_close_reason =
"";
1989 m_local_close_code = m_remote_close_code;
1990 m_local_close_reason = m_remote_close_reason;
1993 std::stringstream s;
1994 s <<
"Closing with code: " << m_local_close_code <<
", and reason: "
1995 << m_local_close_reason;
1998 message_ptr msg = m_msg_manager->get_message();
2003 lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2004 m_local_close_reason,msg);
2013 msg->set_terminal(
true);
2016 m_state = session::state::closing;
2024 if (m_close_handshake_timeout_dur > 0) {
2025 m_handshake_timer = transport_con_type::set_timer(
2026 m_close_handshake_timeout_dur,
2028 &type::handle_close_handshake_timeout,
2030 lib::placeholders::_1
2035 bool needs_writing =
false;
2037 scoped_lock_type lock(m_write_lock);
2039 needs_writing = !m_write_flag && !m_send_queue.empty();
2042 if (needs_writing) {
2043 transport_con_type::dispatch(lib::bind(
2049 return lib::error_code();
2052 template <
typename config>
2053 typename connection<config>::processor_ptr
2054 connection<config>::get_processor(
int version)
const {
2061 p = lib::make_shared<processor::hybi00<config> >(
2062 transport_con_type::is_secure(),
2068 p = lib::make_shared<processor::hybi07<config> >(
2069 transport_con_type::is_secure(),
2076 p = lib::make_shared<processor::hybi08<config> >(
2077 transport_con_type::is_secure(),
2084 p = lib::make_shared<processor::hybi13<config> >(
2085 transport_con_type::is_secure(),
2096 p->set_max_message_size(m_max_message_size);
2101 template <
typename config>
2102 void connection<config>::write_push(
typename config::message_type::ptr msg)
2108 m_send_buffer_size += msg->get_payload().size();
2109 m_send_queue.push(msg);
2112 std::stringstream s;
2113 s <<
"write_push: message count: " << m_send_queue.size()
2114 <<
" buffer size: " << m_send_buffer_size;
2119 template <
typename config>
2120 typename config::message_type::ptr connection<config>::write_pop()
2124 if (m_send_queue.empty()) {
2128 msg = m_send_queue.front();
2130 m_send_buffer_size -= msg->get_payload().size();
2134 std::stringstream s;
2135 s <<
"write_pop: message count: " << m_send_queue.size()
2136 <<
" buffer size: " << m_send_buffer_size;
2142 template <
typename config>
2143 void connection<config>::log_open_result()
2145 std::stringstream s;
2155 s << (version == -1 ?
"HTTP" :
"WebSocket") <<
" Connection ";
2158 s << transport_con_type::get_remote_endpoint() <<
" ";
2161 if (version != -1) {
2162 s <<
"v" << version <<
" ";
2166 std::string ua = m_request.get_header(
"User-Agent");
2175 s << (m_uri ? m_uri->get_resource() :
"NULL") <<
" ";
2178 s << m_response.get_status_code();
2183 template <
typename config>
2184 void connection<config>::log_close_result()
2186 std::stringstream s;
2189 <<
"close local:[" << m_local_close_code
2190 << (m_local_close_reason ==
"" ?
"" :
","+m_local_close_reason)
2191 <<
"] remote:[" << m_remote_close_code
2192 << (m_remote_close_reason ==
"" ?
"" :
","+m_remote_close_reason) <<
"]";
2197 template <
typename config>
2198 void connection<config>::log_fail_result()
2200 std::stringstream s;
2205 s <<
"WebSocket Connection ";
2208 s << transport_con_type::get_remote_endpoint();
2212 s <<
" v" << version;
2216 std::string ua = m_request.get_header(
"User-Agent");
2225 s << (m_uri ? m_uri->get_resource() :
"-");
2228 s <<
" " << m_response.get_status_code();
2231 s <<
" " << m_ec <<
" " << m_ec.message();
2236 template <
typename config>
2237 void connection<config>::log_http_result() {
2238 std::stringstream s;
2246 s << (m_request.get_header(
"host") ==
"" ?
"-" : m_request.get_header(
"host"))
2247 <<
" " << transport_con_type::get_remote_endpoint()
2248 <<
" \"" << m_request.get_method()
2249 <<
" " << (m_uri ? m_uri->get_resource() :
"-")
2250 <<
" " << m_request.get_version() <<
"\" " << m_response.get_status_code()
2251 <<
" " << m_response.get_body().size();
2254 std::string ua = m_request.get_header(
"User-Agent");
2267 #endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
bool is_control(value v)
Check if an opcode is for a control frame.
lib::error_code process_handshake_request()
void add_subprotocol(std::string const &request, lib::error_code &ec)
Adds the given subprotocol string to the request list (exception free)
void set_termination_handler(termination_handler new_handler)
void read_frame()
Issue a new transport read unless reading is paused.
std::vector< std::string > const & get_requested_subprotocols() const
Gets all of the subprotocols requested by the client.
uint16_t value
The type of a close code value.
static level const control
One line per control frame.
std::string const & get_subprotocol() const
Gets the negotated subprotocol.
bool terminal(value code)
Determine if the code represents an unrecoverable error.
uri_ptr get_uri_from_host(request_type &request, std::string scheme)
Extract a URI ptr from the host header of the request.
lib::error_code pause_reading()
Pause reading of new data.
bool is_websocket_handshake(request_type &r)
Determine whether or not a generic HTTP request is a WebSocket handshake.
Attempted to use a client specific feature on a server endpoint.
session::state::value get_state() const
Return the connection state.
static std::vector< int > const versions_supported(helper, helper+4)
Container that stores the list of protocol versions supported.
Selected subprotocol was not requested by the client.
int get_websocket_version(request_type &r)
Extract the version from a WebSocket handshake request.
uri_ptr get_uri() const
Gets the connection URI.
void replace_header(std::string const &key, std::string const &val)
Replace a header.
void ping(std::string const &payload)
Send a ping.
Represents an individual WebSocket connection.
size_t get_buffered_amount() const
Get the size of the outgoing write buffer (in payload bytes)
static level const frame_payload
One line per frame, includes the full message payload (warning: chatty)
static value const protocol_error
A protocol error occurred.
static value const normal
static level const devel
Low level debugging information (warning: very chatty)
std::string string_replace_all(std::string subject, std::string const &search, std::string const &replace)
Replace all occurrances of a substring with another.
status::value extract_code(std::string const &payload, lib::error_code &ec)
Extract a close code value from a close payload.
void select_subprotocol(std::string const &value, lib::error_code &ec)
Select a subprotocol to use (exception free)
std::string to_hex(std::string const &input)
Convert std::string to ascii printed string of hex digits.
void pong(std::string const &payload)
Send a pong.
bool is_not_token_char(unsigned char c)
Is the character a non-token.
void handle_resume_reading()
Resume reading callback.
static level const frame_header
One line per frame, includes the full frame header.
lib::error_code initialize_processor()
static level const devel
Development messages (warning: very chatty)
std::string const & get_request_header(std::string const &key) const
Retrieve a request header.
static level const disconnect
One line for each closed connection. Includes closing codes and reasons.
lib::error_code resume_reading()
Resume reading of new data.
void close(close::status::value const code, std::string const &reason)
Close the connection.
Invalid WebSocket protocol version.
void set_status(http::status_code::value code)
Set response status code and message.
void handle_pong_timeout(std::string payload, lib::error_code const &ec)
Utility method that gets called back when the ping timer expires.
close::status::value to_ws(lib::error_code ec)
Converts a processor error_code into a websocket close code.
lib::error_code make_error_code(error::processor_errors e)
Create an error code with the given value and the processor category.
void append_header(std::string const &key, std::string const &val)
Append a header.
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.
The connection was in the wrong state for this operation.
void write_frame()
Checks if there are frames in the send queue and if there are sends one.
void handle_write_frame(lib::error_code const &ec)
Process the results of a frame write operation and start the next write.
Namespace for the WebSocket++ project.
std::string const & get_origin() const
Return the same origin policy origin value from the opening request.
std::string const & get_response_header(std::string const &key) const
Retrieve a response header.
uint16_t get_port() const
Returns the port component of the connection URI.
std::string const & get_resource() const
Returns the resource component of the connection URI.
A simple utility buffer class.
std::vector< int > const & get_supported_versions() const
Get array of WebSocket protocol versions that this connection supports.
The endpoint is out of outgoing message buffers.
lib::shared_ptr< uri > uri_ptr
Pointer to a URI.
void handle_interrupt()
Transport inturrupt callback.
static value const no_status
A dummy value to indicate that no status code was received.
void handle_pause_reading()
Pause reading callback.
WebSocket close handshake timed out.
bool get_secure() const
Returns the secure flag from the connection URI.
static level const fail
One line for each failed WebSocket connection with details.
static value const abnormal_close
A dummy value to indicate that the connection was closed abnormally.
std::string const & get_request_body() const
Retrieve a request body.
static uint8_t const close_reason_size
Maximum size of close frame reason.
read or write after shutdown
void set_uri(uri_ptr uri)
Sets the connection URI.
WebSocket opening handshake timed out.
std::string const & get_host() const
Returns the host component of the connection URI.
Attempted to use a server specific feature on a client endpoint.
lib::error_code interrupt()
Asyncronously invoke handler::on_inturrupt.
std::string extract_reason(std::string const &payload, lib::error_code &ec)
Extract the reason string from a close payload.
static level const rerror
An invalid uri was supplied.
Unsupported WebSocket protocol version.
static value const blank
A blank value for internal use.
static level const connect
Information about new connections.
static level const http
Access related to HTTP requests.
void remove_header(std::string const &key)
Remove a header.
void set_body(std::string const &value)
Set response body content.