28 #ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP
29 #define WEBSOCKETPP_CONNECTION_IMPL_HPP
31 #include <websocketpp/common/platforms.hpp>
32 #include <websocketpp/common/system_error.hpp>
34 #include <websocketpp/processors/processor.hpp>
36 #include <websocketpp/processors/hybi00.hpp>
37 #include <websocketpp/processors/hybi07.hpp>
38 #include <websocketpp/processors/hybi08.hpp>
39 #include <websocketpp/processors/hybi13.hpp>
43 namespace istate = session::internal_state;
45 template <
typename config>
47 termination_handler new_handler)
50 "connection set_termination_handler");
54 m_termination_handler = new_handler;
57 template <
typename config>
60 return m_processor->get_origin(m_request);
63 template <
typename config>
66 return m_send_buffer_size;
69 template <
typename config>
75 template <
typename config>
77 frame::opcode::value op)
79 message_ptr msg = m_msg_manager->get_message(op,payload.size());
80 msg->append_payload(payload);
85 template <
typename config>
87 frame::opcode::value op)
89 message_ptr msg = m_msg_manager->get_message(op,len);
90 msg->append_payload(payload,len);
95 template <
typename config>
103 if (m_state != session::state::open) {
107 message_ptr outgoing_msg;
108 bool needs_writing =
false;
110 if (msg->get_prepared()) {
113 scoped_lock_type lock(m_write_lock);
114 write_push(outgoing_msg);
115 needs_writing = !m_write_flag && !m_send_queue.empty();
117 outgoing_msg = m_msg_manager->get_message();
123 scoped_lock_type lock(m_write_lock);
124 lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg);
130 write_push(outgoing_msg);
131 needs_writing = !m_write_flag && !m_send_queue.empty();
135 transport_con_type::dispatch(lib::bind(
141 return lib::error_code();
144 template <
typename config>
150 if (m_state != session::state::open) {
155 message_ptr msg = m_msg_manager->get_message();
161 ec = m_processor->prepare_ping(payload,msg);
165 if (m_pong_timeout_handler) {
168 m_ping_timer->cancel();
171 if (m_pong_timeout_dur > 0) {
172 m_ping_timer = transport_con_type::set_timer(
175 &type::handle_pong_timeout,
178 lib::placeholders::_1
186 set but the transport in use does not support timeouts.");
190 bool needs_writing =
false;
192 scoped_lock_type lock(m_write_lock);
194 needs_writing = !m_write_flag && !m_send_queue.empty();
198 transport_con_type::dispatch(lib::bind(
204 ec = lib::error_code();
207 template<
typename config>
216 template<
typename config>
230 if (m_pong_timeout_handler) {
231 m_pong_timeout_handler(m_connection_hdl,payload);
235 template <
typename config>
241 if (m_state != session::state::open) {
246 message_ptr msg = m_msg_manager->get_message();
252 ec = m_processor->prepare_pong(payload,msg);
255 bool needs_writing =
false;
257 scoped_lock_type lock(m_write_lock);
259 needs_writing = !m_write_flag && !m_send_queue.empty();
263 transport_con_type::dispatch(lib::bind(
269 ec = lib::error_code();
272 template<
typename config>
281 template <
typename config>
283 std::string
const & reason, lib::error_code & ec)
289 if (m_state != session::state::open) {
295 std::string tr(reason,0,std::min<size_t>(reason.size(),
301 template<
typename config>
303 std::string
const & reason)
306 close(code,reason,ec);
316 template <
typename config>
319 return transport_con_type::interrupt(
321 &type::handle_interrupt,
328 template <
typename config>
330 if (m_interrupt_handler) {
331 m_interrupt_handler(m_connection_hdl);
335 template <
typename config>
338 return transport_con_type::dispatch(
340 &type::handle_pause_reading,
347 template <
typename config>
353 template <
typename config>
356 return transport_con_type::dispatch(
358 &type::handle_resume_reading,
365 template <
typename config>
381 template <
typename config>
384 return m_uri->get_secure();
387 template <
typename config>
390 return m_uri->get_host();
393 template <
typename config>
396 return m_uri->get_resource();
399 template <
typename config>
402 return m_uri->get_port();
405 template <
typename config>
411 template <
typename config>
422 template <
typename config>
424 return m_subprotocol;
427 template <
typename config>
428 const std::vector<std::string> &
430 return m_requested_subprotocols;
433 template <
typename config>
435 lib::error_code & ec)
443 if (value.empty() || std::find_if(value.begin(),value.end(),
450 m_requested_subprotocols.push_back(value);
453 template <
typename config>
456 this->add_subprotocol(value,ec);
463 template <
typename config>
465 lib::error_code & ec)
473 ec = lib::error_code();
477 std::vector<std::string>::iterator it;
479 it = std::find(m_requested_subprotocols.begin(),
480 m_requested_subprotocols.end(),
483 if (it == m_requested_subprotocols.end()) {
488 m_subprotocol =
value;
491 template <
typename config>
494 this->select_subprotocol(value,ec);
501 template <
typename config>
504 return m_request.get_header(key);
507 template <
typename config>
510 return m_response.get_header(key);
513 template <
typename config>
518 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
524 m_response.set_status(code);
526 template <
typename config>
528 std::string
const & msg)
532 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
538 m_response.set_status(code,msg);
540 template <
typename config>
544 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
550 m_response.set_body(value);
553 template <
typename config>
555 std::string
const & val)
560 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
562 m_response.append_header(key,val);
567 if (m_internal_state == istate::USER_INIT) {
569 m_request.append_header(key,val);
575 template <
typename config>
577 std::string
const & val)
582 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
584 m_response.replace_header(key,val);
589 if (m_internal_state == istate::USER_INIT) {
591 m_request.replace_header(key,val);
597 template <
typename config>
603 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
605 m_response.remove_header(key);
610 if (m_internal_state == istate::USER_INIT) {
612 m_request.remove_header(key);
626 template <
typename config>
630 this->atomic_state_change(
632 istate::TRANSPORT_INIT,
633 "Start must be called from user init state"
639 transport_con_type::init(
641 &type::handle_transport_init,
643 lib::placeholders::_1
648 template <
typename config>
649 void connection<config>::handle_transport_init(lib::error_code
const & ec) {
653 scoped_lock_type lock(m_connection_state_lock);
655 if (m_internal_state != istate::TRANSPORT_INIT) {
664 m_internal_state = istate::READ_HTTP_REQUEST;
666 m_internal_state = istate::WRITE_HTTP_REQUEST;
673 s <<
"handle_transport_init received error: "<< ec.message();
682 this->read_handshake(1);
686 m_processor = get_processor(config::client_version);
687 this->send_http_request();
691 template <
typename config>
692 void connection<config>::read_handshake(
size_t num_bytes) {
695 if (m_open_handshake_timeout_dur > 0) {
696 m_handshake_timer = transport_con_type::set_timer(
697 m_open_handshake_timeout_dur,
699 &type::handle_open_handshake_timeout,
701 lib::placeholders::_1
706 transport_con_type::async_read_at_least(
709 config::connection_read_buffer_size,
711 &type::handle_read_handshake,
713 lib::placeholders::_1,
714 lib::placeholders::_2
721 template <
typename config>
722 void connection<config>::handle_read_handshake(lib::error_code
const & ec,
723 size_t bytes_transferred)
727 this->atomic_state_check(
728 istate::READ_HTTP_REQUEST,
729 "handle_read_handshake must be called from READ_HTTP_REQUEST state"
735 if (m_state == session::state::closed) {
742 s <<
"error in handle_read_handshake: "<< ec.message();
749 if (bytes_transferred > config::connection_read_buffer_size) {
755 size_t bytes_processed = 0;
757 bytes_processed = m_request.consume(m_buf,bytes_transferred);
758 }
catch (http::exception &e) {
761 m_response.set_status(e.m_error_code,e.m_error_msg);
762 this->send_http_response_error();
768 if (bytes_processed > config::connection_read_buffer_size) {
776 s <<
"bytes_transferred: " << bytes_transferred
777 <<
" bytes, bytes processed: " << bytes_processed <<
" bytes";
781 if (m_request.ready()) {
782 if (!this->initialize_processor()) {
783 this->send_http_response_error();
787 if (m_processor && m_processor->get_version() == 0) {
790 if (bytes_transferred-bytes_processed >= 8) {
791 m_request.replace_header(
792 "Sec-WebSocket-Key3",
793 std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
795 bytes_processed += 8;
799 m_response.set_status(http::status_code::internal_server_error);
800 this->send_http_response_error();
807 if (m_request.get_header(
"Sec-WebSocket-Key3") !=
"") {
816 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
817 m_buf_cursor = bytes_transferred-bytes_processed;
819 this->atomic_state_change(
820 istate::READ_HTTP_REQUEST,
821 istate::PROCESS_HTTP_REQUEST,
822 "send_http_response must be called from READ_HTTP_REQUEST state"
826 this->process_handshake_request();
827 this->send_http_response();
830 transport_con_type::async_read_at_least(
833 config::connection_read_buffer_size,
835 &type::handle_read_handshake,
837 lib::placeholders::_1,
838 lib::placeholders::_2
849 template <
typename config>
850 void connection<config>::send_http_response_error() {
851 this->atomic_state_change(
852 istate::READ_HTTP_REQUEST,
853 istate::PROCESS_HTTP_REQUEST,
854 "send_http_response must be called from READ_HTTP_REQUEST state"
856 this->send_http_response();
861 template <
typename config>
862 void connection<config>::handle_read_frame(lib::error_code
const & ec,
863 size_t bytes_transferred)
867 this->atomic_state_check(
868 istate::PROCESS_CONNECTION,
869 "handle_read_frame must be called from PROCESS_CONNECTION state"
876 if (m_state == session::state::closed) {
881 }
else if (m_state == session::state::closing && !m_is_server) {
885 terminate(lib::error_code());
890 if (m_state == session::state::closed) {
894 terminate(lib::error_code());
902 log_err(echannel,
"handle_read_frame", ec);
918 s <<
"p = " << p <<
" bytes transferred = " << bytes_transferred;
922 while (p < bytes_transferred) {
925 s <<
"calling consume with " << bytes_transferred-p <<
" bytes";
929 lib::error_code consume_ec;
931 p += m_processor->consume(
932 reinterpret_cast<uint8_t*>(m_buf)+p,
939 s <<
"bytes left after consume: " << bytes_transferred-p;
945 if (config::drop_on_protocol_error) {
946 this->terminate(consume_ec);
949 lib::error_code close_ec;
952 consume_ec.message(),
958 this->terminate(close_ec);
965 if (m_processor->ready()) {
968 s <<
"Complete message received. Dispatching";
972 message_ptr msg = m_processor->get_message();
978 if (m_state != session::state::open) {
980 }
else if (m_message_handler) {
981 m_message_handler(m_connection_hdl, msg);
984 process_control_frame(msg);
993 template <
typename config>
999 transport_con_type::async_read_at_least(
1009 config::connection_read_buffer_size,
1014 template <
typename config>
1027 m_response.set_status(http::status_code::bad_request);
1031 m_processor = get_processor(version);
1041 m_response.set_status(http::status_code::bad_request);
1043 std::stringstream ss;
1044 std::string sep =
"";
1045 std::vector<int>::const_iterator it;
1052 m_response.replace_header(
"Sec-WebSocket-Version",ss.str());
1056 template <
typename config>
1067 (transport_con_type::is_secure() ?
"https" :
"http")
1070 if (!m_uri->get_valid()) {
1072 m_response.set_status(http::status_code::bad_request);
1076 if (m_http_handler) {
1077 m_http_handler(m_connection_hdl);
1079 set_status(http::status_code::upgrade_required);
1085 lib::error_code ec = m_processor->validate_handshake(m_request);
1091 m_response.set_status(http::status_code::bad_request);
1097 std::pair<lib::error_code,std::string> neg_results;
1098 neg_results = m_processor->negotiate_extensions(m_request);
1100 if (neg_results.first) {
1104 m_response.set_status(http::status_code::bad_request);
1110 if (neg_results.second.size() > 0) {
1111 m_response.replace_header(
"Sec-WebSocket-Extensions",
1112 neg_results.second);
1117 m_uri = m_processor->get_uri(m_request);
1120 if (!m_uri->get_valid()) {
1122 m_response.set_status(http::status_code::bad_request);
1127 lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1128 m_requested_subprotocols);
1135 if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1136 m_response.set_status(http::status_code::switching_protocols);
1140 ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1143 std::stringstream s;
1144 s <<
"Processing error: " << ec <<
"(" << ec.message() <<
")";
1147 m_response.set_status(http::status_code::internal_server_error);
1157 if (m_response.get_status_code() == http::status_code::uninitialized) {
1158 m_response.set_status(http::status_code::bad_request);
1167 template <
typename config>
1171 if (m_response.get_status_code() == http::status_code::uninitialized) {
1172 m_response.
set_status(http::status_code::internal_server_error);
1175 m_response.set_version(
"HTTP/1.1");
1178 if (m_response.get_header(
"Server") ==
"") {
1179 if (!m_user_agent.empty()) {
1180 m_response.replace_header(
"Server",m_user_agent);
1182 m_response.remove_header(
"Server");
1188 m_handshake_buffer = m_processor->get_raw(m_response);
1191 m_handshake_buffer = m_response.raw();
1196 if (m_response.get_header(
"Sec-WebSocket-Key3") !=
"") {
1203 transport_con_type::async_write(
1204 m_handshake_buffer.data(),
1205 m_handshake_buffer.size(),
1207 &type::handle_send_http_response,
1209 lib::placeholders::_1
1214 template <
typename config>
1215 void connection<config>::handle_send_http_response(lib::error_code
const & ec) {
1218 this->atomic_state_check(
1219 istate::PROCESS_HTTP_REQUEST,
1220 "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
1225 this->terminate(ec);
1229 this->log_open_result();
1231 if (m_handshake_timer) {
1232 m_handshake_timer->cancel();
1233 m_handshake_timer.reset();
1236 if (m_response.get_status_code() != http::status_code::switching_protocols)
1240 std::stringstream s;
1241 s <<
"Handshake ended with HTTP error: "
1242 << m_response.get_status_code();
1252 this->atomic_state_change(
1253 istate::PROCESS_HTTP_REQUEST,
1254 istate::PROCESS_CONNECTION,
1255 session::state::connecting,
1256 session::state::open,
1257 "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
1260 if (m_open_handler) {
1261 m_open_handler(m_connection_hdl);
1264 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1267 template <
typename config>
1268 void connection<config>::send_http_request() {
1277 ec = m_processor->client_handshake_request(m_request,m_uri,
1278 m_requested_subprotocols);
1290 if (m_request.get_header(
"User-Agent") ==
"") {
1291 if (!m_user_agent.empty()) {
1292 m_request.replace_header(
"User-Agent",m_user_agent);
1294 m_request.remove_header(
"User-Agent");
1298 m_handshake_buffer = m_request.raw();
1304 if (m_open_handshake_timeout_dur > 0) {
1305 m_handshake_timer = transport_con_type::set_timer(
1306 m_open_handshake_timeout_dur,
1308 &type::handle_open_handshake_timeout,
1310 lib::placeholders::_1
1315 transport_con_type::async_write(
1316 m_handshake_buffer.data(),
1317 m_handshake_buffer.size(),
1319 &type::handle_send_http_request,
1321 lib::placeholders::_1
1326 template <
typename config>
1327 void connection<config>::handle_send_http_request(lib::error_code
const & ec) {
1330 this->atomic_state_check(
1331 istate::WRITE_HTTP_REQUEST,
1332 "handle_send_http_request must be called from WRITE_HTTP_REQUEST state"
1337 this->terminate(ec);
1341 this->atomic_state_change(
1342 istate::WRITE_HTTP_REQUEST,
1343 istate::READ_HTTP_RESPONSE,
1344 "handle_send_http_request must be called from WRITE_HTTP_REQUEST state"
1347 transport_con_type::async_read_at_least(
1350 config::connection_read_buffer_size,
1352 &type::handle_read_http_response,
1354 lib::placeholders::_1,
1355 lib::placeholders::_2
1360 template <
typename config>
1361 void connection<config>::handle_read_http_response(lib::error_code
const & ec,
1362 size_t bytes_transferred)
1366 this->atomic_state_check(
1367 istate::READ_HTTP_RESPONSE,
1368 "handle_read_http_response must be called from READ_HTTP_RESPONSE state"
1373 this->terminate(ec);
1376 size_t bytes_processed = 0;
1379 bytes_processed = m_response.consume(m_buf,bytes_transferred);
1380 }
catch (http::exception & e) {
1382 std::string(
"error in handle_read_http_response: ")+e.what());
1389 if (m_response.headers_ready()) {
1390 if (m_handshake_timer) {
1391 m_handshake_timer->cancel();
1392 m_handshake_timer.reset();
1395 lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1401 this->terminate(validate_ec);
1406 this->atomic_state_change(
1407 istate::READ_HTTP_RESPONSE,
1408 istate::PROCESS_CONNECTION,
1409 session::state::connecting,
1410 session::state::open,
1411 "handle_read_http_response must be called from READ_HTTP_RESPONSE state"
1414 this->log_open_result();
1416 if (m_open_handler) {
1417 m_open_handler(m_connection_hdl);
1423 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1424 m_buf_cursor = bytes_transferred-bytes_processed;
1426 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1428 transport_con_type::async_read_at_least(
1431 config::connection_read_buffer_size,
1433 &type::handle_read_http_response,
1435 lib::placeholders::_1,
1436 lib::placeholders::_2
1442 template <
typename config>
1443 void connection<config>::handle_open_handshake_timeout(
1444 lib::error_code
const & ec)
1450 "open handle_open_handshake_timeout error: "+ec.message());
1458 template <
typename config>
1459 void connection<config>::handle_close_handshake_timeout(
1460 lib::error_code
const & ec)
1466 "asio open handle_close_handshake_timeout error: "+ec.message());
1474 template <
typename config>
1475 void connection<config>::terminate(lib::error_code
const & ec) {
1481 if (m_handshake_timer) {
1482 m_handshake_timer->cancel();
1483 m_handshake_timer.reset();
1486 terminate_status tstat = unknown;
1490 m_local_close_reason = ec.message();
1493 if (m_state == session::state::connecting) {
1494 m_state = session::state::closed;
1496 }
else if (m_state != session::state::closed) {
1497 m_state = session::state::closed;
1501 "terminate called on connection that was already terminated");
1507 transport_con_type::async_shutdown(
1509 &type::handle_terminate,
1512 lib::placeholders::_1
1517 template <
typename config>
1518 void connection<config>::handle_terminate(terminate_status tstat,
1519 lib::error_code
const & ec)
1531 if (tstat == failed) {
1532 if (m_fail_handler) {
1533 m_fail_handler(m_connection_hdl);
1536 }
else if (tstat == closed) {
1537 if (m_close_handler) {
1538 m_close_handler(m_connection_hdl);
1548 if (m_termination_handler) {
1550 m_termination_handler(type::get_shared());
1551 }
catch (std::exception
const & e) {
1553 std::string(
"termination_handler call failed. Reason was: ")+e.what());
1558 template <
typename config>
1563 scoped_lock_type lock(m_write_lock);
1575 message_ptr next_message = write_pop();
1576 while (next_message) {
1577 m_current_msgs.push_back(next_message);
1578 if (!next_message->get_terminal()) {
1579 next_message = write_pop();
1581 next_message = message_ptr();
1585 if (m_current_msgs.empty()) {
1592 m_write_flag =
true;
1596 typename std::vector<message_ptr>::iterator it;
1597 for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1598 std::string
const & header = (*it)->get_header();
1599 std::string
const & payload = (*it)->get_payload();
1608 std::stringstream general,header,payload;
1610 general <<
"Dispatching write containing " << m_current_msgs.size()
1611 <<
" message(s) containing ";
1612 header <<
"Header Bytes: \n";
1613 payload <<
"Payload Bytes: \n";
1618 for (
size_t i = 0; i < m_current_msgs.size(); i++) {
1619 hbytes += m_current_msgs[i]->get_header().size();
1620 pbytes += m_current_msgs[i]->get_payload().size();
1623 header <<
"[" << i <<
"] ("
1624 << m_current_msgs[i]->get_header().size() <<
") "
1629 payload <<
"[" << i <<
"] ("
1630 << m_current_msgs[i]->get_payload().size() <<
") "
1637 general << hbytes <<
" header bytes and " << pbytes <<
" payload bytes";
1645 transport_con_type::async_write(
1647 m_write_frame_handler
1651 template <
typename config>
1658 bool terminal = m_current_msgs.back()->get_terminal();
1660 m_send_buffer.clear();
1661 m_current_msgs.clear();
1666 this->terminate(ec);
1671 this->terminate(lib::error_code());
1675 bool needs_writing =
false;
1677 scoped_lock_type lock(m_write_lock);
1680 m_write_flag =
false;
1682 needs_writing = !m_send_queue.empty();
1685 if (needs_writing) {
1686 transport_con_type::dispatch(lib::bind(
1693 template <
typename config>
1697 scoped_lock_type lock(m_connection_state_lock);
1699 if (m_internal_state != req) {
1704 m_internal_state = dest;
1707 template <
typename config>
1709 istate_type internal_dest, session::state::value external_req,
1710 session::state::value external_dest, std::string msg)
1712 scoped_lock_type lock(m_connection_state_lock);
1714 if (m_internal_state != internal_req || m_state != external_req) {
1719 m_internal_state = internal_dest;
1720 m_state = external_dest;
1723 template <
typename config>
1726 scoped_lock_type lock(m_connection_state_lock);
1728 if (m_internal_state != req) {
1734 template <
typename config>
1740 template <
typename config>
1745 frame::opcode::value op = msg->get_opcode();
1748 std::stringstream s;
1749 s <<
"Control frame received with opcode " << op;
1752 if (m_state == session::state::closed) {
1756 if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1761 if (op == frame::opcode::PING) {
1762 bool should_reply =
true;
1764 if (m_ping_handler) {
1765 should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1769 this->pong(msg->get_payload(),ec);
1774 }
else if (op == frame::opcode::PONG) {
1775 if (m_pong_handler) {
1776 m_pong_handler(m_connection_hdl, msg->get_payload());
1779 m_ping_timer->cancel();
1781 }
else if (op == frame::opcode::CLOSE) {
1788 if (config::drop_on_protocol_error) {
1789 s <<
"Received invalid close code " << m_remote_close_code
1790 <<
" dropping connection per config.";
1792 this->terminate(ec);
1794 s <<
"Received invalid close code " << m_remote_close_code
1795 <<
" sending acknowledgement and closing";
1798 "Invalid close code");
1808 if (config::drop_on_protocol_error) {
1810 "Received invalid close reason. Dropping connection per config");
1811 this->terminate(ec);
1814 "Received invalid close reason. Sending acknowledgement and closing");
1816 "Invalid close reason");
1824 if (m_state == session::state::open) {
1826 s <<
"Received close frame with code " << m_remote_close_code
1827 <<
" and reason " << m_remote_close_reason;
1830 ec = send_close_ack();
1834 }
else if (m_state == session::state::closing && !m_was_clean) {
1848 terminate(lib::error_code());
1861 template <
typename config>
1863 std::string
const & reason)
1865 return send_close_frame(code,reason,
true,m_is_server);
1868 template <
typename config>
1870 std::string
const & reason,
bool ack,
bool terminal)
1882 if (config::silent_close) {
1885 m_local_close_reason =
"";
1888 m_local_close_code = code;
1889 m_local_close_reason = reason;
1893 m_local_close_reason =
"";
1896 "acknowledging a no-status close with normal code");
1898 m_local_close_reason =
"";
1901 m_local_close_code = m_remote_close_code;
1902 m_local_close_reason = m_remote_close_reason;
1905 std::stringstream s;
1906 s <<
"Closing with code: " << m_local_close_code <<
", and reason: "
1907 << m_local_close_reason;
1910 message_ptr msg = m_msg_manager->get_message();
1915 lib::error_code ec = m_processor->prepare_close(m_local_close_code,
1916 m_local_close_reason,msg);
1925 msg->set_terminal(
true);
1928 m_state = session::state::closing;
1936 if (m_close_handshake_timeout_dur > 0) {
1937 m_handshake_timer = transport_con_type::set_timer(
1938 m_close_handshake_timeout_dur,
1940 &type::handle_close_handshake_timeout,
1942 lib::placeholders::_1
1947 bool needs_writing =
false;
1949 scoped_lock_type lock(m_write_lock);
1951 needs_writing = !m_write_flag && !m_send_queue.empty();
1954 if (needs_writing) {
1955 transport_con_type::dispatch(lib::bind(
1961 return lib::error_code();
1964 template <
typename config>
1965 typename connection<config>::processor_ptr
1966 connection<config>::get_processor(
int version)
const {
1973 p.reset(
new processor::hybi00<config>(
1974 transport_con_type::is_secure(),
1980 p.reset(
new processor::hybi07<config>(
1981 transport_con_type::is_secure(),
1988 p.reset(
new processor::hybi08<config>(
1989 transport_con_type::is_secure(),
1996 p.reset(
new processor::hybi13<config>(
1997 transport_con_type::is_secure(),
2008 p->set_max_message_size(m_max_message_size);
2013 template <
typename config>
2014 void connection<config>::write_push(
typename config::message_type::ptr msg)
2020 m_send_buffer_size += msg->get_payload().size();
2021 m_send_queue.push(msg);
2024 std::stringstream s;
2025 s <<
"write_push: message count: " << m_send_queue.size()
2026 <<
" buffer size: " << m_send_buffer_size;
2031 template <
typename config>
2032 typename config::message_type::ptr connection<config>::write_pop()
2036 if (m_send_queue.empty()) {
2040 msg = m_send_queue.front();
2042 m_send_buffer_size -= msg->get_payload().size();
2046 std::stringstream s;
2047 s <<
"write_pop: message count: " << m_send_queue.size()
2048 <<
" buffer size: " << m_send_buffer_size;
2054 template <
typename config>
2055 void connection<config>::log_open_result()
2057 std::stringstream s;
2067 s << (version == -1 ?
"HTTP" :
"WebSocket") <<
" Connection ";
2070 s << transport_con_type::get_remote_endpoint() <<
" ";
2073 if (version != -1) {
2074 s <<
"v" << version <<
" ";
2078 std::string ua = m_request.get_header(
"User-Agent");
2087 s << (m_uri ? m_uri->get_resource() :
"NULL") <<
" ";
2090 s << m_response.get_status_code();
2095 template <
typename config>
2096 void connection<config>::log_close_result()
2098 std::stringstream s;
2101 <<
"close local:[" << m_local_close_code
2102 << (m_local_close_reason ==
"" ?
"" :
","+m_local_close_reason)
2103 <<
"] remote:[" << m_remote_close_code
2104 << (m_remote_close_reason ==
"" ?
"" :
","+m_remote_close_reason) <<
"]";
2109 template <
typename config>
2110 void connection<config>::log_fail_result()
2119 #endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
bool is_control(value v)
Check if an opcode is for a control frame.
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::string const & get_subprotocol() const
Gets the negotated subprotocol.
uint16_t value
The type of a close code value.
std::string const & get_origin() const
Return the same origin policy origin value from the opening request.
static level const control
One line per control frame.
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 process_handshake_request()
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)
std::string const & get_request_header(std::string const &key)
Retrieve a request header.
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, const std::string &search, const std::string &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(const std::string &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 atomic_state_check(istate_type req, std::string msg)
Atomically read and compared the internal state.
void handle_resume_reading()
Resume reading callback.
static level const frame_header
One line per frame, includes the full frame header.
std::string const & get_resource() const
Returns the resource component of the connection URI.
static level const devel
Development messages (warning: very chatty)
static level const disconnect
One line for each closed connection. Includes closing codes and reasons.
const std::vector< int > & get_supported_versions() const
Get array of WebSocket protocol versions that this connection supports.
std::string const & get_host() const
Returns the host component of the connection URI.
lib::error_code resume_reading()
Resume reading of new data.
void close(close::status::value const code, std::string const &reason)
Close the connection.
void atomic_state_change(istate_type req, istate_type dest, std::string msg)
Atomically change the internal connection state.
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.
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.
uint16_t get_port() const
Returns the port component of the connection URI.
std::string const & get_response_header(std::string const &key)
Retrieve a response header.
A simple utility buffer class.
The endpoint is out of outgoing message buffers.
bool initialize_processor()
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.
std::vector< std::string > const & get_requested_subprotocols() const
Gets all of the subprotocols requested by the client.
bool get_secure() const
Returns the secure flag from the connection URI.
static value const abnormal_close
A dummy value to indicate that the connection was closed abnormally.
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.
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
static value const blank
A blank value for internal use.
static level const connect
Information about new connections.
void remove_header(std::string const &key)
Remove a header.
void set_body(std::string const &value)
Set response body content.