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>
218 lib::error_code
const & ec)
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) {
519 throw exception(
"Call to set_status from invalid state",
523 m_response.set_status(code);
525 template <
typename config>
527 std::string
const & msg)
531 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
532 throw exception(
"Call to set_status from invalid state",
536 m_response.set_status(code,msg);
538 template <
typename config>
542 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
543 throw exception(
"Call to set_status from invalid state",
547 m_response.set_body(value);
550 template <
typename config>
552 std::string
const & val)
557 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
559 m_response.append_header(key,val);
561 throw exception(
"Call to append_header from invalid state",
565 if (m_internal_state == istate::USER_INIT) {
567 m_request.append_header(key,val);
569 throw exception(
"Call to append_header from invalid state",
574 template <
typename config>
576 std::string
const & val)
581 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
583 m_response.replace_header(key,val);
585 throw exception(
"Call to replace_header from invalid state",
589 if (m_internal_state == istate::USER_INIT) {
591 m_request.replace_header(key,val);
593 throw exception(
"Call to replace_header from invalid state",
598 template <
typename config>
604 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
606 m_response.remove_header(key);
608 throw exception(
"Call to remove_header from invalid state",
612 if (m_internal_state == istate::USER_INIT) {
614 m_request.remove_header(key);
616 throw exception(
"Call to remove_header from invalid state",
629 template <
typename config>
633 this->atomic_state_change(
635 istate::TRANSPORT_INIT,
636 "Start must be called from user init state"
642 transport_con_type::init(
644 &type::handle_transport_init,
646 lib::placeholders::_1
651 template <
typename config>
652 void connection<config>::handle_transport_init(lib::error_code
const & ec) {
656 scoped_lock_type lock(m_connection_state_lock);
658 if (m_internal_state != istate::TRANSPORT_INIT) {
659 throw exception(
"handle_transport_init must be called from transport init state",
666 m_internal_state = istate::READ_HTTP_REQUEST;
668 m_internal_state = istate::WRITE_HTTP_REQUEST;
675 s <<
"handle_transport_init received error: "<< ec.message();
684 this->read_handshake(1);
688 m_processor = get_processor(config::client_version);
689 this->send_http_request();
693 template <
typename config>
694 void connection<config>::read_handshake(
size_t num_bytes) {
697 if (m_open_handshake_timeout_dur > 0) {
698 m_handshake_timer = transport_con_type::set_timer(
699 m_open_handshake_timeout_dur,
701 &type::handle_open_handshake_timeout,
703 lib::placeholders::_1
708 transport_con_type::async_read_at_least(
711 config::connection_read_buffer_size,
713 &type::handle_read_handshake,
715 lib::placeholders::_1,
716 lib::placeholders::_2
723 template <
typename config>
724 void connection<config>::handle_read_handshake(lib::error_code
const & ec,
725 size_t bytes_transferred)
729 this->atomic_state_check(
730 istate::READ_HTTP_REQUEST,
731 "handle_read_handshake must be called from READ_HTTP_REQUEST state"
737 if (m_state == session::state::closed) {
744 s <<
"error in handle_read_handshake: "<< ec.message();
751 if (bytes_transferred > config::connection_read_buffer_size) {
757 size_t bytes_processed = 0;
759 bytes_processed = m_request.consume(m_buf,bytes_transferred);
760 }
catch (http::exception &e) {
763 m_response.set_status(e.m_error_code,e.m_error_msg);
764 this->send_http_response_error();
770 if (bytes_processed > config::connection_read_buffer_size) {
778 s <<
"bytes_transferred: " << bytes_transferred
779 <<
" bytes, bytes processed: " << bytes_processed <<
" bytes";
783 if (m_request.ready()) {
784 if (!this->initialize_processor()) {
785 this->send_http_response_error();
789 if (m_processor && m_processor->get_version() == 0) {
792 if (bytes_transferred-bytes_processed >= 8) {
793 m_request.replace_header(
794 "Sec-WebSocket-Key3",
795 std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
797 bytes_processed += 8;
801 m_response.set_status(http::status_code::internal_server_error);
802 this->send_http_response_error();
809 if (m_request.get_header(
"Sec-WebSocket-Key3") !=
"") {
818 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
819 m_buf_cursor = bytes_transferred-bytes_processed;
821 this->atomic_state_change(
822 istate::READ_HTTP_REQUEST,
823 istate::PROCESS_HTTP_REQUEST,
824 "send_http_response must be called from READ_HTTP_REQUEST state"
828 this->process_handshake_request();
829 this->send_http_response();
832 transport_con_type::async_read_at_least(
835 config::connection_read_buffer_size,
837 &type::handle_read_handshake,
839 lib::placeholders::_1,
840 lib::placeholders::_2
851 template <
typename config>
852 void connection<config>::send_http_response_error() {
853 this->atomic_state_change(
854 istate::READ_HTTP_REQUEST,
855 istate::PROCESS_HTTP_REQUEST,
856 "send_http_response must be called from READ_HTTP_REQUEST state"
858 this->send_http_response();
863 template <
typename config>
864 void connection<config>::handle_read_frame(lib::error_code
const & ec,
865 size_t bytes_transferred)
869 this->atomic_state_check(
870 istate::PROCESS_CONNECTION,
871 "handle_read_frame must be called from PROCESS_CONNECTION state"
878 if (m_state == session::state::closed) {
883 }
else if (m_state == session::state::closing && !m_is_server) {
887 terminate(lib::error_code());
892 if (m_state == session::state::closed) {
896 terminate(lib::error_code());
904 log_err(echannel,
"handle_read_frame", ec);
920 s <<
"p = " << p <<
" bytes transferred = " << bytes_transferred;
924 while (p < bytes_transferred) {
927 s <<
"calling consume with " << bytes_transferred-p <<
" bytes";
931 lib::error_code consume_ec;
933 p += m_processor->consume(
934 reinterpret_cast<uint8_t*>(m_buf)+p,
941 s <<
"bytes left after consume: " << bytes_transferred-p;
947 if (config::drop_on_protocol_error) {
948 this->terminate(consume_ec);
951 lib::error_code close_ec;
954 consume_ec.message(),
960 this->terminate(close_ec);
967 if (m_processor->ready()) {
970 s <<
"Complete message received. Dispatching";
974 message_ptr msg = m_processor->get_message();
980 if (m_state != session::state::open) {
982 }
else if (m_message_handler) {
983 m_message_handler(m_connection_hdl, msg);
986 process_control_frame(msg);
995 template <
typename config>
1001 transport_con_type::async_read_at_least(
1011 config::connection_read_buffer_size,
1016 template <
typename config>
1029 m_response.set_status(http::status_code::bad_request);
1033 m_processor = get_processor(version);
1043 m_response.set_status(http::status_code::bad_request);
1045 std::stringstream ss;
1046 std::string sep =
"";
1047 std::vector<int>::const_iterator it;
1054 m_response.replace_header(
"Sec-WebSocket-Version",ss.str());
1058 template <
typename config>
1069 (transport_con_type::is_secure() ?
"https" :
"http")
1072 if (!m_uri->get_valid()) {
1074 m_response.set_status(http::status_code::bad_request);
1078 if (m_http_handler) {
1079 m_http_handler(m_connection_hdl);
1081 set_status(http::status_code::upgrade_required);
1087 lib::error_code ec = m_processor->validate_handshake(m_request);
1093 m_response.set_status(http::status_code::bad_request);
1099 std::pair<lib::error_code,std::string> neg_results;
1100 neg_results = m_processor->negotiate_extensions(m_request);
1102 if (neg_results.first) {
1106 m_response.set_status(http::status_code::bad_request);
1112 if (neg_results.second.size() > 0) {
1113 m_response.replace_header(
"Sec-WebSocket-Extensions",
1114 neg_results.second);
1119 m_uri = m_processor->get_uri(m_request);
1122 if (!m_uri->get_valid()) {
1124 m_response.set_status(http::status_code::bad_request);
1129 lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1130 m_requested_subprotocols);
1137 if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1138 m_response.set_status(http::status_code::switching_protocols);
1142 ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1145 std::stringstream s;
1146 s <<
"Processing error: " << ec <<
"(" << ec.message() <<
")";
1149 m_response.set_status(http::status_code::internal_server_error);
1159 if (m_response.get_status_code() == http::status_code::uninitialized) {
1160 m_response.set_status(http::status_code::bad_request);
1169 template <
typename config>
1173 if (m_response.get_status_code() == http::status_code::uninitialized) {
1174 m_response.
set_status(http::status_code::internal_server_error);
1177 m_response.set_version(
"HTTP/1.1");
1180 if (m_response.get_header(
"Server") ==
"") {
1181 if (!m_user_agent.empty()) {
1182 m_response.replace_header(
"Server",m_user_agent);
1184 m_response.remove_header(
"Server");
1190 m_handshake_buffer = m_processor->get_raw(m_response);
1193 m_handshake_buffer = m_response.raw();
1198 if (m_response.get_header(
"Sec-WebSocket-Key3") !=
"") {
1205 transport_con_type::async_write(
1206 m_handshake_buffer.data(),
1207 m_handshake_buffer.size(),
1209 &type::handle_send_http_response,
1211 lib::placeholders::_1
1216 template <
typename config>
1217 void connection<config>::handle_send_http_response(lib::error_code
const & ec) {
1220 this->atomic_state_check(
1221 istate::PROCESS_HTTP_REQUEST,
1222 "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
1227 this->terminate(ec);
1231 this->log_open_result();
1233 if (m_handshake_timer) {
1234 m_handshake_timer->cancel();
1235 m_handshake_timer.reset();
1238 if (m_response.get_status_code() != http::status_code::switching_protocols)
1242 std::stringstream s;
1243 s <<
"Handshake ended with HTTP error: "
1244 << m_response.get_status_code();
1254 this->atomic_state_change(
1255 istate::PROCESS_HTTP_REQUEST,
1256 istate::PROCESS_CONNECTION,
1257 session::state::connecting,
1258 session::state::open,
1259 "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
1262 if (m_open_handler) {
1263 m_open_handler(m_connection_hdl);
1266 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1269 template <
typename config>
1270 void connection<config>::send_http_request() {
1279 ec = m_processor->client_handshake_request(m_request,m_uri,
1280 m_requested_subprotocols);
1292 if (m_request.get_header(
"User-Agent") ==
"") {
1293 if (!m_user_agent.empty()) {
1294 m_request.replace_header(
"User-Agent",m_user_agent);
1296 m_request.remove_header(
"User-Agent");
1300 m_handshake_buffer = m_request.raw();
1306 if (m_open_handshake_timeout_dur > 0) {
1307 m_handshake_timer = transport_con_type::set_timer(
1308 m_open_handshake_timeout_dur,
1310 &type::handle_open_handshake_timeout,
1312 lib::placeholders::_1
1317 transport_con_type::async_write(
1318 m_handshake_buffer.data(),
1319 m_handshake_buffer.size(),
1321 &type::handle_send_http_request,
1323 lib::placeholders::_1
1328 template <
typename config>
1329 void connection<config>::handle_send_http_request(lib::error_code
const & ec) {
1332 this->atomic_state_check(
1333 istate::WRITE_HTTP_REQUEST,
1334 "handle_send_http_request must be called from WRITE_HTTP_REQUEST state"
1339 this->terminate(ec);
1343 this->atomic_state_change(
1344 istate::WRITE_HTTP_REQUEST,
1345 istate::READ_HTTP_RESPONSE,
1346 "handle_send_http_request must be called from WRITE_HTTP_REQUEST state"
1349 transport_con_type::async_read_at_least(
1352 config::connection_read_buffer_size,
1354 &type::handle_read_http_response,
1356 lib::placeholders::_1,
1357 lib::placeholders::_2
1362 template <
typename config>
1363 void connection<config>::handle_read_http_response(lib::error_code
const & ec,
1364 size_t bytes_transferred)
1368 this->atomic_state_check(
1369 istate::READ_HTTP_RESPONSE,
1370 "handle_read_http_response must be called from READ_HTTP_RESPONSE state"
1375 this->terminate(ec);
1378 size_t bytes_processed = 0;
1381 bytes_processed = m_response.consume(m_buf,bytes_transferred);
1382 }
catch (http::exception & e) {
1384 std::string(
"error in handle_read_http_response: ")+e.what());
1391 if (m_response.headers_ready()) {
1392 if (m_handshake_timer) {
1393 m_handshake_timer->cancel();
1394 m_handshake_timer.reset();
1397 lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1403 this->terminate(validate_ec);
1408 this->atomic_state_change(
1409 istate::READ_HTTP_RESPONSE,
1410 istate::PROCESS_CONNECTION,
1411 session::state::connecting,
1412 session::state::open,
1413 "handle_read_http_response must be called from READ_HTTP_RESPONSE state"
1416 this->log_open_result();
1418 if (m_open_handler) {
1419 m_open_handler(m_connection_hdl);
1425 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1426 m_buf_cursor = bytes_transferred-bytes_processed;
1428 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1430 transport_con_type::async_read_at_least(
1433 config::connection_read_buffer_size,
1435 &type::handle_read_http_response,
1437 lib::placeholders::_1,
1438 lib::placeholders::_2
1444 template <
typename config>
1445 void connection<config>::handle_open_handshake_timeout(
1446 lib::error_code
const & ec)
1452 "open handle_open_handshake_timeout error: "+ec.message());
1460 template <
typename config>
1461 void connection<config>::handle_close_handshake_timeout(
1462 lib::error_code
const & ec)
1468 "asio open handle_close_handshake_timeout error: "+ec.message());
1476 template <
typename config>
1477 void connection<config>::terminate(lib::error_code
const & ec) {
1483 if (m_handshake_timer) {
1484 m_handshake_timer->cancel();
1485 m_handshake_timer.reset();
1488 terminate_status tstat = unknown;
1492 m_local_close_reason = ec.message();
1495 if (m_state == session::state::connecting) {
1496 m_state = session::state::closed;
1498 }
else if (m_state != session::state::closed) {
1499 m_state = session::state::closed;
1503 "terminate called on connection that was already terminated");
1509 transport_con_type::async_shutdown(
1511 &type::handle_terminate,
1514 lib::placeholders::_1
1519 template <
typename config>
1520 void connection<config>::handle_terminate(terminate_status tstat,
1521 lib::error_code
const & ec)
1533 if (tstat == failed) {
1534 if (m_fail_handler) {
1535 m_fail_handler(m_connection_hdl);
1538 }
else if (tstat == closed) {
1539 if (m_close_handler) {
1540 m_close_handler(m_connection_hdl);
1550 if (m_termination_handler) {
1552 m_termination_handler(type::get_shared());
1553 }
catch (std::exception
const & e) {
1555 std::string(
"termination_handler call failed. Reason was: ")+e.what());
1560 template <
typename config>
1565 scoped_lock_type lock(m_write_lock);
1577 message_ptr next_message = write_pop();
1578 while (next_message) {
1579 m_current_msgs.push_back(next_message);
1580 if (!next_message->get_terminal()) {
1581 next_message = write_pop();
1583 next_message = message_ptr();
1587 if (m_current_msgs.empty()) {
1594 m_write_flag =
true;
1598 typename std::vector<message_ptr>::iterator it;
1599 for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1600 std::string
const & header = (*it)->get_header();
1601 std::string
const & payload = (*it)->get_payload();
1610 std::stringstream general,header,payload;
1612 general <<
"Dispatching write containing " << m_current_msgs.size()
1613 <<
" message(s) containing ";
1614 header <<
"Header Bytes: \n";
1615 payload <<
"Payload Bytes: \n";
1620 for (
size_t i = 0; i < m_current_msgs.size(); i++) {
1621 hbytes += m_current_msgs[i]->get_header().size();
1622 pbytes += m_current_msgs[i]->get_payload().size();
1625 header <<
"[" << i <<
"] ("
1626 << m_current_msgs[i]->get_header().size() <<
") "
1631 payload <<
"[" << i <<
"] ("
1632 << m_current_msgs[i]->get_payload().size() <<
") "
1639 general << hbytes <<
" header bytes and " << pbytes <<
" payload bytes";
1647 transport_con_type::async_write(
1649 m_write_frame_handler
1653 template <
typename config>
1660 bool terminal = m_current_msgs.back()->get_terminal();
1662 m_send_buffer.clear();
1663 m_current_msgs.clear();
1668 this->terminate(ec);
1673 this->terminate(lib::error_code());
1677 bool needs_writing =
false;
1679 scoped_lock_type lock(m_write_lock);
1682 m_write_flag =
false;
1684 needs_writing = !m_send_queue.empty();
1687 if (needs_writing) {
1688 transport_con_type::dispatch(lib::bind(
1695 template <
typename config>
1699 scoped_lock_type lock(m_connection_state_lock);
1701 if (m_internal_state != req) {
1705 m_internal_state = dest;
1708 template <
typename config>
1710 istate_type internal_dest, session::state::value external_req,
1711 session::state::value external_dest, std::string msg)
1713 scoped_lock_type lock(m_connection_state_lock);
1715 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) {
1733 template <
typename config>
1739 template <
typename config>
1744 frame::opcode::value op = msg->get_opcode();
1747 std::stringstream s;
1748 s <<
"Control frame received with opcode " << op;
1751 if (m_state == session::state::closed) {
1755 if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1760 if (op == frame::opcode::PING) {
1761 bool should_reply =
true;
1763 if (m_ping_handler) {
1764 should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1768 this->pong(msg->get_payload(),ec);
1773 }
else if (op == frame::opcode::PONG) {
1774 if (m_pong_handler) {
1775 m_pong_handler(m_connection_hdl, msg->get_payload());
1778 m_ping_timer->cancel();
1780 }
else if (op == frame::opcode::CLOSE) {
1787 if (config::drop_on_protocol_error) {
1788 s <<
"Received invalid close code " << m_remote_close_code
1789 <<
" dropping connection per config.";
1791 this->terminate(ec);
1793 s <<
"Received invalid close code " << m_remote_close_code
1794 <<
" sending acknowledgement and closing";
1797 "Invalid close code");
1807 if (config::drop_on_protocol_error) {
1809 "Received invalid close reason. Dropping connection per config");
1810 this->terminate(ec);
1813 "Received invalid close reason. Sending acknowledgement and closing");
1815 "Invalid close reason");
1823 if (m_state == session::state::open) {
1825 s <<
"Received close frame with code " << m_remote_close_code
1826 <<
" and reason " << m_remote_close_reason;
1829 ec = send_close_ack();
1833 }
else if (m_state == session::state::closing && !m_was_clean) {
1847 terminate(lib::error_code());
1860 template <
typename config>
1862 std::string
const & reason)
1864 return send_close_frame(code,reason,
true,m_is_server);
1867 template <
typename config>
1869 std::string
const & reason,
bool ack,
bool terminal)
1881 if (config::silent_close) {
1884 m_local_close_reason =
"";
1887 m_local_close_code = code;
1888 m_local_close_reason = reason;
1892 m_local_close_reason =
"";
1895 "acknowledging a no-status close with normal code");
1897 m_local_close_reason =
"";
1900 m_local_close_code = m_remote_close_code;
1901 m_local_close_reason = m_remote_close_reason;
1904 std::stringstream s;
1905 s <<
"Closing with code: " << m_local_close_code <<
", and reason: "
1906 << m_local_close_reason;
1909 message_ptr msg = m_msg_manager->get_message();
1914 lib::error_code ec = m_processor->prepare_close(m_local_close_code,
1915 m_local_close_reason,msg);
1924 msg->set_terminal(
true);
1927 m_state = session::state::closing;
1935 if (m_close_handshake_timeout_dur > 0) {
1936 m_handshake_timer = transport_con_type::set_timer(
1937 m_close_handshake_timeout_dur,
1939 &type::handle_close_handshake_timeout,
1941 lib::placeholders::_1
1946 bool needs_writing =
false;
1948 scoped_lock_type lock(m_write_lock);
1950 needs_writing = !m_write_flag && !m_send_queue.empty();
1953 if (needs_writing) {
1954 transport_con_type::dispatch(lib::bind(
1960 return lib::error_code();
1963 template <
typename config>
1964 typename connection<config>::processor_ptr
1965 connection<config>::get_processor(
int version)
const {
1972 p = lib::make_shared<processor::hybi00<config> >(
1973 transport_con_type::is_secure(),
1979 p = lib::make_shared<processor::hybi07<config> >(
1980 transport_con_type::is_secure(),
1987 p = lib::make_shared<processor::hybi08<config> >(
1988 transport_con_type::is_secure(),
1995 p = lib::make_shared<processor::hybi13<config> >(
1996 transport_con_type::is_secure(),
2007 p->set_max_message_size(m_max_message_size);
2012 template <
typename config>
2013 void connection<config>::write_push(
typename config::message_type::ptr msg)
2019 m_send_buffer_size += msg->get_payload().size();
2020 m_send_queue.push(msg);
2023 std::stringstream s;
2024 s <<
"write_push: message count: " << m_send_queue.size()
2025 <<
" buffer size: " << m_send_buffer_size;
2030 template <
typename config>
2031 typename config::message_type::ptr connection<config>::write_pop()
2035 if (m_send_queue.empty()) {
2039 msg = m_send_queue.front();
2041 m_send_buffer_size -= msg->get_payload().size();
2045 std::stringstream s;
2046 s <<
"write_pop: message count: " << m_send_queue.size()
2047 <<
" buffer size: " << m_send_buffer_size;
2053 template <
typename config>
2054 void connection<config>::log_open_result()
2056 std::stringstream s;
2066 s << (version == -1 ?
"HTTP" :
"WebSocket") <<
" Connection ";
2069 s << transport_con_type::get_remote_endpoint() <<
" ";
2072 if (version != -1) {
2073 s <<
"v" << version <<
" ";
2077 std::string ua = m_request.get_header(
"User-Agent");
2086 s << (m_uri ? m_uri->get_resource() :
"NULL") <<
" ";
2089 s << m_response.get_status_code();
2094 template <
typename config>
2095 void connection<config>::log_close_result()
2097 std::stringstream s;
2100 <<
"close local:[" << m_local_close_code
2101 << (m_local_close_reason ==
"" ?
"" :
","+m_local_close_reason)
2102 <<
"] remote:[" << m_remote_close_code
2103 << (m_remote_close_reason ==
"" ?
"" :
","+m_remote_close_reason) <<
"]";
2108 template <
typename config>
2109 void connection<config>::log_fail_result()
2118 #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.