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> 48 namespace websocketpp {
50 namespace istate = session::internal_state;
52 template <
typename config>
53 void connection<config>::set_termination_handler(
54 termination_handler new_handler)
56 m_alog.write(log::alevel::devel,
57 "connection set_termination_handler");
61 m_termination_handler = new_handler;
64 template <
typename config>
65 std::string
const & connection<config>::get_origin()
const {
67 return m_processor->get_origin(m_request);
70 template <
typename config>
71 size_t connection<config>::get_buffered_amount()
const {
73 return m_send_buffer_size;
76 template <
typename config>
77 session::state::value connection<config>::get_state()
const {
82 template <
typename config>
83 lib::error_code connection<config>::send(std::string
const & payload,
84 frame::opcode::value op)
86 message_ptr msg = m_msg_manager->get_message(op,payload.size());
87 msg->append_payload(payload);
88 msg->set_compressed(
true);
93 template <
typename config>
94 lib::error_code connection<config>::send(
void const * payload, size_t len,
95 frame::opcode::value op)
97 message_ptr msg = m_msg_manager->get_message(op,len);
98 msg->append_payload(payload,len);
103 template <
typename config>
104 lib::error_code connection<config>::send(
typename config::message_type::ptr msg)
106 if (m_alog.static_test(log::alevel::devel)) {
107 m_alog.write(log::alevel::devel,
"connection send");
111 scoped_lock_type lock(m_connection_state_lock);
112 if (m_state != session::state::open) {
113 return error::make_error_code(error::invalid_state);
117 message_ptr outgoing_msg;
118 bool needs_writing =
false;
120 if (msg->get_prepared()) {
123 scoped_lock_type lock(m_write_lock);
124 write_push(outgoing_msg);
125 needs_writing = !m_write_flag && !m_send_queue.empty();
127 outgoing_msg = m_msg_manager->get_message();
130 return error::make_error_code(error::no_outgoing_buffers);
133 scoped_lock_type lock(m_write_lock);
134 lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg);
140 write_push(outgoing_msg);
141 needs_writing = !m_write_flag && !m_send_queue.empty();
145 transport_con_type::dispatch(lib::bind(
151 return lib::error_code();
154 template <
typename config>
155 void connection<config>::ping(std::string
const& payload, lib::error_code& ec) {
156 if (m_alog.static_test(log::alevel::devel)) {
157 m_alog.write(log::alevel::devel,
"connection ping");
161 scoped_lock_type lock(m_connection_state_lock);
162 if (m_state != session::state::open) {
163 std::stringstream ss;
164 ss <<
"connection::ping called from invalid state " << m_state;
165 m_alog.write(log::alevel::devel,ss.str());
166 ec = error::make_error_code(error::invalid_state);
171 message_ptr msg = m_msg_manager->get_message();
173 ec = error::make_error_code(error::no_outgoing_buffers);
177 ec = m_processor->prepare_ping(payload,msg);
181 if (m_pong_timeout_handler) {
184 m_ping_timer->cancel();
187 if (m_pong_timeout_dur > 0) {
188 m_ping_timer = transport_con_type::set_timer(
191 &type::handle_pong_timeout,
194 lib::placeholders::_1
201 m_elog.write(log::elevel::warn,
"Warning: a pong_timeout_handler is \ 202 set but the transport in use does not support timeouts.");
206 bool needs_writing =
false;
208 scoped_lock_type lock(m_write_lock);
210 needs_writing = !m_write_flag && !m_send_queue.empty();
214 transport_con_type::dispatch(lib::bind(
220 ec = lib::error_code();
223 template<
typename config>
224 void connection<config>::ping(std::string
const & payload) {
232 template<
typename config>
233 void connection<config>::handle_pong_timeout(std::string payload,
234 lib::error_code
const & ec)
237 if (ec == transport::error::operation_aborted) {
242 m_elog.write(log::elevel::devel,
"pong_timeout error: "+ec.message());
246 if (m_pong_timeout_handler) {
247 m_pong_timeout_handler(m_connection_hdl,payload);
251 template <
typename config>
252 void connection<config>::pong(std::string
const& payload, lib::error_code& ec) {
253 if (m_alog.static_test(log::alevel::devel)) {
254 m_alog.write(log::alevel::devel,
"connection pong");
258 scoped_lock_type lock(m_connection_state_lock);
259 if (m_state != session::state::open) {
260 std::stringstream ss;
261 ss <<
"connection::pong called from invalid state " << m_state;
262 m_alog.write(log::alevel::devel,ss.str());
263 ec = error::make_error_code(error::invalid_state);
268 message_ptr msg = m_msg_manager->get_message();
270 ec = error::make_error_code(error::no_outgoing_buffers);
274 ec = m_processor->prepare_pong(payload,msg);
277 bool needs_writing =
false;
279 scoped_lock_type lock(m_write_lock);
281 needs_writing = !m_write_flag && !m_send_queue.empty();
285 transport_con_type::dispatch(lib::bind(
291 ec = lib::error_code();
294 template<
typename config>
295 void connection<config>::pong(std::string
const & payload) {
303 template <
typename config>
304 void connection<config>::close(close::status::value
const code,
305 std::string
const & reason, lib::error_code & ec)
307 if (m_alog.static_test(log::alevel::devel)) {
308 m_alog.write(log::alevel::devel,
"connection close");
312 std::string tr(reason,0,std::min<size_t>(reason.size(),
313 frame::limits::close_reason_size));
315 scoped_lock_type lock(m_connection_state_lock);
317 if (m_state != session::state::open) {
318 ec = error::make_error_code(error::invalid_state);
322 ec =
this->send_close_frame(code,tr,
false,close::status::terminal(code));
325 template<
typename config>
326 void connection<config>::close(close::status::value
const code,
327 std::string
const & reason)
330 close(code,reason,ec);
340 template <
typename config>
341 lib::error_code connection<config>::interrupt() {
342 m_alog.write(log::alevel::devel,
"connection connection::interrupt");
343 return transport_con_type::interrupt(
345 &type::handle_interrupt,
352 template <
typename config>
353 void connection<config>::handle_interrupt() {
354 if (m_interrupt_handler) {
355 m_interrupt_handler(m_connection_hdl);
359 template <
typename config>
360 lib::error_code connection<config>::pause_reading() {
361 m_alog.write(log::alevel::devel,
"connection connection::pause_reading");
362 return transport_con_type::dispatch(
364 &type::handle_pause_reading,
371 template <
typename config>
372 void connection<config>::handle_pause_reading() {
373 m_alog.write(log::alevel::devel,
"connection connection::handle_pause_reading");
377 template <
typename config>
378 lib::error_code connection<config>::resume_reading() {
379 m_alog.write(log::alevel::devel,
"connection connection::resume_reading");
380 return transport_con_type::dispatch(
382 &type::handle_resume_reading,
389 template <
typename config>
390 void connection<config>::handle_resume_reading() {
405 template <
typename config>
406 bool connection<config>::get_secure()
const {
408 return m_uri->get_secure();
411 template <
typename config>
412 std::string
const & connection<config>::get_host()
const {
414 return m_uri->get_host();
417 template <
typename config>
418 std::string
const & connection<config>::get_resource()
const {
420 return m_uri->get_resource();
423 template <
typename config>
424 uint16_t connection<config>::get_port()
const {
426 return m_uri->get_port();
429 template <
typename config>
430 uri_ptr connection<config>::get_uri()
const {
435 template <
typename config>
436 void connection<config>::set_uri(uri_ptr uri) {
446 template <
typename config>
447 std::string
const & connection<config>::get_subprotocol()
const {
448 return m_subprotocol;
451 template <
typename config>
452 std::vector<std::string>
const &
453 connection<config>::get_requested_subprotocols()
const {
454 return m_requested_subprotocols;
457 template <
typename config>
458 void connection<config>::add_subprotocol(std::string
const & value,
459 lib::error_code & ec)
462 ec = error::make_error_code(error::client_only);
467 if (value.empty() || std::find_if(value.begin(),value.end(),
468 http::is_not_token_char) != value.end())
470 ec = error::make_error_code(error::invalid_subprotocol);
474 m_requested_subprotocols.push_back(value);
477 template <
typename config>
478 void connection<config>::add_subprotocol(std::string
const & value) {
480 this->add_subprotocol(value,ec);
487 template <
typename config>
488 void connection<config>::select_subprotocol(std::string
const & value,
489 lib::error_code & ec)
492 ec = error::make_error_code(error::server_only);
497 ec = lib::error_code();
501 std::vector<std::string>::iterator it;
503 it = std::find(m_requested_subprotocols.begin(),
504 m_requested_subprotocols.end(),
507 if (it == m_requested_subprotocols.end()) {
508 ec = error::make_error_code(error::unrequested_subprotocol);
512 m_subprotocol = value;
515 template <
typename config>
516 void connection<config>::select_subprotocol(std::string
const & value) {
518 this->select_subprotocol(value,ec);
525 template <
typename config>
527 connection<config>::get_request_header(std::string
const & key)
const {
528 return m_request.get_header(key);
531 template <
typename config>
533 connection<config>::get_request_body()
const {
534 return m_request.get_body();
537 template <
typename config>
539 connection<config>::get_response_header(std::string
const & key)
const {
540 return m_response.get_header(key);
544 template <
typename config>
545 void connection<config>::set_status(http::status_code::value code)
547 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
548 throw exception(
"Call to set_status from invalid state",
549 error::make_error_code(error::invalid_state));
551 m_response.set_status(code);
555 template <
typename config>
556 void connection<config>::set_status(http::status_code::value code,
557 std::string
const & msg)
559 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
560 throw exception(
"Call to set_status from invalid state",
561 error::make_error_code(error::invalid_state));
564 m_response.set_status(code,msg);
568 template <
typename config>
569 void connection<config>::set_body(std::string
const & value) {
570 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
571 throw exception(
"Call to set_status from invalid state",
572 error::make_error_code(error::invalid_state));
575 m_response.set_body(value);
579 template <
typename config>
580 void connection<config>::append_header(std::string
const & key,
581 std::string
const & val)
584 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
586 m_response.append_header(key,val);
588 throw exception(
"Call to append_header from invalid state",
589 error::make_error_code(error::invalid_state));
592 if (m_internal_state == istate::USER_INIT) {
594 m_request.append_header(key,val);
596 throw exception(
"Call to append_header from invalid state",
597 error::make_error_code(error::invalid_state));
603 template <
typename config>
604 void connection<config>::replace_header(std::string
const & key,
605 std::string
const & val)
608 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
610 m_response.replace_header(key,val);
612 throw exception(
"Call to replace_header from invalid state",
613 error::make_error_code(error::invalid_state));
616 if (m_internal_state == istate::USER_INIT) {
618 m_request.replace_header(key,val);
620 throw exception(
"Call to replace_header from invalid state",
621 error::make_error_code(error::invalid_state));
627 template <
typename config>
628 void connection<config>::remove_header(std::string
const & key)
631 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
633 m_response.remove_header(key);
635 throw exception(
"Call to remove_header from invalid state",
636 error::make_error_code(error::invalid_state));
639 if (m_internal_state == istate::USER_INIT) {
641 m_request.remove_header(key);
643 throw exception(
"Call to remove_header from invalid state",
644 error::make_error_code(error::invalid_state));
660 template <
typename config>
661 lib::error_code connection<config>::defer_http_response() {
664 if (m_handshake_timer) {
665 m_handshake_timer->cancel();
666 m_handshake_timer.reset();
670 m_http_state = session::http_state::deferred;
672 return lib::error_code();
685 template <
typename config>
686 void connection<config>::send_http_response(lib::error_code & ec) {
688 scoped_lock_type lock(m_connection_state_lock);
689 if (m_http_state != session::http_state::deferred) {
690 ec = error::make_error_code(error::invalid_state);
694 m_http_state = session::http_state::body_written;
697 this->write_http_response(lib::error_code());
698 ec = lib::error_code();
701 template <
typename config>
702 void connection<config>::send_http_response() {
704 this->send_http_response(ec);
715 template <
typename config>
716 void connection<config>::start() {
717 m_alog.write(log::alevel::devel,
"connection start");
719 if (m_internal_state != istate::USER_INIT) {
720 m_alog.write(log::alevel::devel,
"Start called in invalid state");
721 this->terminate(error::make_error_code(error::invalid_state));
725 m_internal_state = istate::TRANSPORT_INIT;
730 transport_con_type::init(
732 &type::handle_transport_init,
734 lib::placeholders::_1
739 template <
typename config>
740 void connection<config>::handle_transport_init(lib::error_code
const & ec) {
741 m_alog.write(log::alevel::devel,
"connection handle_transport_init");
743 lib::error_code ecm = ec;
745 if (m_internal_state != istate::TRANSPORT_INIT) {
746 m_alog.write(log::alevel::devel,
747 "handle_transport_init must be called from transport init state");
748 ecm = error::make_error_code(error::invalid_state);
753 s <<
"handle_transport_init received error: "<< ecm.message();
754 m_elog.write(log::elevel::rerror,s.str());
756 this->terminate(ecm);
762 m_internal_state = istate::READ_HTTP_REQUEST;
763 this->read_handshake(1);
767 m_internal_state = istate::WRITE_HTTP_REQUEST;
768 m_processor = get_processor(config::client_version);
769 this->send_http_request();
773 template <
typename config>
774 void connection<config>::read_handshake(size_t num_bytes) {
775 m_alog.write(log::alevel::devel,
"connection read_handshake");
777 if (m_open_handshake_timeout_dur > 0) {
778 m_handshake_timer = transport_con_type::set_timer(
779 m_open_handshake_timeout_dur,
781 &type::handle_open_handshake_timeout,
783 lib::placeholders::_1
788 transport_con_type::async_read_at_least(
791 config::connection_read_buffer_size,
793 &type::handle_read_handshake,
795 lib::placeholders::_1,
796 lib::placeholders::_2
803 template <
typename config>
804 void connection<config>::handle_read_handshake(lib::error_code
const & ec,
805 size_t bytes_transferred)
807 m_alog.write(log::alevel::devel,
"connection handle_read_handshake");
809 lib::error_code ecm = ec;
812 scoped_lock_type lock(m_connection_state_lock);
814 if (m_state == session::state::connecting) {
815 if (m_internal_state != istate::READ_HTTP_REQUEST) {
816 ecm = error::make_error_code(error::invalid_state);
818 }
else if (m_state == session::state::closed) {
822 m_alog.write(log::alevel::devel,
823 "handle_read_handshake invoked after connection was closed");
826 ecm = error::make_error_code(error::invalid_state);
831 if (ecm == transport::error::eof && m_state == session::state::closed) {
833 m_alog.write(log::alevel::devel,
834 "got (expected) eof/state error from closed con");
838 log_err(log::elevel::rerror,
"handle_read_handshake",ecm);
839 this->terminate(ecm);
844 if (bytes_transferred > config::connection_read_buffer_size) {
845 m_elog.write(log::elevel::fatal,
"Fatal boundaries checking error.");
846 this->terminate(make_error_code(error::general));
850 size_t bytes_processed = 0;
852 bytes_processed = m_request.consume(m_buf,bytes_transferred);
853 }
catch (http::exception &e) {
856 m_response.set_status(e.m_error_code,e.m_error_msg);
857 this->write_http_response_error(error::make_error_code(error::http_parse_error));
863 if (bytes_processed > bytes_transferred) {
864 m_elog.write(log::elevel::fatal,
"Fatal boundaries checking error.");
865 this->terminate(make_error_code(error::general));
869 if (m_alog.static_test(log::alevel::devel)) {
871 s <<
"bytes_transferred: " << bytes_transferred
872 <<
" bytes, bytes processed: " << bytes_processed <<
" bytes";
873 m_alog.write(log::alevel::devel,s.str());
876 if (m_request.ready()) {
877 lib::error_code processor_ec =
this->initialize_processor();
879 this->write_http_response_error(processor_ec);
883 if (m_processor && m_processor->get_version() == 0) {
886 if (bytes_transferred-bytes_processed >= 8) {
887 m_request.replace_header(
888 "Sec-WebSocket-Key3",
889 std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
891 bytes_processed += 8;
894 m_alog.write(log::alevel::devel,
"short key3 read");
895 m_response.set_status(http::status_code::internal_server_error);
896 this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3));
901 if (m_alog.static_test(log::alevel::devel)) {
902 m_alog.write(log::alevel::devel,m_request.raw());
903 if (!m_request.get_header(
"Sec-WebSocket-Key3").empty()) {
904 m_alog.write(log::alevel::devel,
905 utility::to_hex(m_request.get_header(
"Sec-WebSocket-Key3")));
912 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
913 m_buf_cursor = bytes_transferred-bytes_processed;
916 m_internal_state = istate::PROCESS_HTTP_REQUEST;
919 lib::error_code handshake_ec =
this->process_handshake_request();
924 if (!m_is_http || m_http_state == session::http_state::init) {
925 this->write_http_response(handshake_ec);
929 transport_con_type::async_read_at_least(
932 config::connection_read_buffer_size,
934 &type::handle_read_handshake,
936 lib::placeholders::_1,
937 lib::placeholders::_2
948 template <
typename config>
949 void connection<config>::write_http_response_error(lib::error_code
const & ec) {
950 if (m_internal_state != istate::READ_HTTP_REQUEST) {
951 m_alog.write(log::alevel::devel,
952 "write_http_response_error called in invalid state");
953 this->terminate(error::make_error_code(error::invalid_state));
957 m_internal_state = istate::PROCESS_HTTP_REQUEST;
959 this->write_http_response(ec);
964 template <
typename config>
965 void connection<config>::handle_read_frame(lib::error_code
const & ec,
966 size_t bytes_transferred)
970 lib::error_code ecm = ec;
972 if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
973 ecm = error::make_error_code(error::invalid_state);
977 log::level echannel = log::elevel::rerror;
979 if (ecm == transport::error::eof) {
980 if (m_state == session::state::closed) {
983 m_alog.write(log::alevel::devel,
"got eof from closed con");
985 }
else if (m_state == session::state::closing && !m_is_server) {
989 terminate(lib::error_code());
992 }
else if (ecm == error::invalid_state) {
997 if (m_state == session::state::closed) {
998 m_alog.write(log::alevel::devel,
999 "handle_read_frame: got invalid istate in closed state");
1002 }
else if (ecm == transport::error::tls_short_read) {
1003 if (m_state == session::state::closed) {
1007 terminate(lib::error_code());
1010 echannel = log::elevel::rerror;
1011 }
else if (ecm == transport::error::action_after_shutdown) {
1012 echannel = log::elevel::info;
1015 log_err(echannel,
"handle_read_frame", ecm);
1016 this->terminate(ecm);
1029 if (m_alog.static_test(log::alevel::devel)) {
1030 std::stringstream s;
1031 s <<
"p = " << p <<
" bytes transferred = " << bytes_transferred;
1032 m_alog.write(log::alevel::devel,s.str());
1035 while (p < bytes_transferred) {
1036 if (m_alog.static_test(log::alevel::devel)) {
1037 std::stringstream s;
1038 s <<
"calling consume with " << bytes_transferred-p <<
" bytes";
1039 m_alog.write(log::alevel::devel,s.str());
1042 lib::error_code consume_ec;
1044 if (m_alog.static_test(log::alevel::devel)) {
1045 std::stringstream s;
1046 s <<
"Processing Bytes: " << utility::to_hex(
reinterpret_cast<uint8_t*>(m_buf)+p,bytes_transferred-p);
1047 m_alog.write(log::alevel::devel,s.str());
1050 p += m_processor->consume(
1051 reinterpret_cast<uint8_t*>(m_buf)+p,
1052 bytes_transferred-p,
1056 if (m_alog.static_test(log::alevel::devel)) {
1057 std::stringstream s;
1058 s <<
"bytes left after consume: " << bytes_transferred-p;
1059 m_alog.write(log::alevel::devel,s.str());
1062 log_err(log::elevel::rerror,
"consume", consume_ec);
1064 if (config::drop_on_protocol_error) {
1065 this->terminate(consume_ec);
1068 lib::error_code close_ec;
1070 processor::error::to_ws(consume_ec),
1071 consume_ec.message(),
1076 log_err(log::elevel::fatal,
"Protocol error close frame ", close_ec);
1077 this->terminate(close_ec);
1084 if (m_processor->ready()) {
1085 if (m_alog.static_test(log::alevel::devel)) {
1086 std::stringstream s;
1087 s <<
"Complete message received. Dispatching";
1088 m_alog.write(log::alevel::devel,s.str());
1091 message_ptr msg = m_processor->get_message();
1094 m_alog.write(log::alevel::devel,
"null message from m_processor");
1095 }
else if (!is_control(msg->get_opcode())) {
1097 if (m_state != session::state::open) {
1098 m_elog.write(log::elevel::warn,
"got non-close frame while closing");
1099 }
else if (m_message_handler) {
1100 m_message_handler(m_connection_hdl, msg);
1103 process_control_frame(msg);
1112 template <
typename config>
1113 void connection<config>::read_frame() {
1118 transport_con_type::async_read_at_least(
1128 config::connection_read_buffer_size,
1133 template <
typename config>
1134 lib::error_code connection<config>::initialize_processor() {
1135 m_alog.write(log::alevel::devel,
"initialize_processor");
1138 if (!processor::is_websocket_handshake(m_request)) {
1139 return lib::error_code();
1142 int version = processor::get_websocket_version(m_request);
1145 m_alog.write(log::alevel::devel,
"BAD REQUEST: can't determine version");
1146 m_response.set_status(http::status_code::bad_request);
1147 return error::make_error_code(error::invalid_version);
1150 m_processor = get_processor(version);
1154 return lib::error_code();
1159 m_alog.write(log::alevel::devel,
"BAD REQUEST: no processor for version");
1160 m_response.set_status(http::status_code::bad_request);
1162 std::stringstream ss;
1164 std::vector<
int>::const_iterator it;
1165 for (it = versions_supported.begin(); it != versions_supported.end(); it++)
1171 m_response.replace_header(
"Sec-WebSocket-Version",ss.str());
1172 return error::make_error_code(error::unsupported_version);
1175 template <
typename config>
1176 lib::error_code connection<config>::process_handshake_request() {
1177 m_alog.write(log::alevel::devel,
"process handshake request");
1179 if (!processor::is_websocket_handshake(m_request)) {
1181 m_alog.write(log::alevel::devel,
"HTTP REQUEST");
1184 m_uri = processor::get_uri_from_host(
1186 (transport_con_type::is_secure() ?
"https" :
"http")
1189 if (!m_uri->get_valid()) {
1190 m_alog.write(log::alevel::devel,
"Bad request: failed to parse uri");
1191 m_response.set_status(http::status_code::bad_request);
1192 return error::make_error_code(error::invalid_uri);
1195 if (m_http_handler) {
1197 m_http_handler(m_connection_hdl);
1199 if (m_state == session::state::closed) {
1200 return error::make_error_code(error::http_connection_ended);
1203 set_status(http::status_code::upgrade_required);
1204 return error::make_error_code(error::upgrade_required);
1207 return lib::error_code();
1210 lib::error_code ec = m_processor->validate_handshake(m_request);
1215 m_alog.write(log::alevel::devel,
"Bad request " + ec.message());
1216 m_response.set_status(http::status_code::bad_request);
1222 std::pair<lib::error_code,std::string> neg_results;
1223 neg_results = m_processor->negotiate_extensions(m_request);
1225 if (neg_results.first == processor::error::make_error_code(processor::error::extension_parse_error)) {
1228 m_elog.write(log::elevel::info,
"Bad request: " + neg_results.first.message());
1229 m_response.set_status(http::status_code::bad_request);
1230 return neg_results.first;
1231 }
else if (neg_results.first) {
1235 m_elog.write(log::elevel::info,
1236 "Extension negotiation failed: " + neg_results.first.message());
1241 if (neg_results.second.size() > 0) {
1242 m_response.replace_header(
"Sec-WebSocket-Extensions",
1243 neg_results.second);
1248 m_uri = m_processor->get_uri(m_request);
1251 if (!m_uri->get_valid()) {
1252 m_alog.write(log::alevel::devel,
"Bad request: failed to parse uri");
1253 m_response.set_status(http::status_code::bad_request);
1254 return error::make_error_code(error::invalid_uri);
1258 lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1259 m_requested_subprotocols);
1266 if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1267 m_response.set_status(http::status_code::switching_protocols);
1271 ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1274 std::stringstream s;
1275 s <<
"Processing error: " << ec <<
"(" << ec.message() <<
")";
1276 m_alog.write(log::alevel::devel, s.str());
1278 m_response.set_status(http::status_code::internal_server_error);
1283 m_alog.write(log::alevel::devel,
"USER REJECT");
1288 if (m_response.get_status_code() == http::status_code::uninitialized) {
1289 m_response.set_status(http::status_code::bad_request);
1292 return error::make_error_code(error::rejected);
1295 return lib::error_code();
1298 template <
typename config>
1299 void connection<config>::write_http_response(lib::error_code
const & ec) {
1300 m_alog.write(log::alevel::devel,
"connection write_http_response");
1302 if (ec == error::make_error_code(error::http_connection_ended)) {
1303 m_alog.write(log::alevel::http,
"An HTTP handler took over the connection.");
1307 if (m_response.get_status_code() == http::status_code::uninitialized) {
1308 m_response.set_status(http::status_code::internal_server_error);
1309 m_ec = error::make_error_code(error::general);
1314 m_response.set_version(
"HTTP/1.1");
1317 if (m_response.get_header(
"Server").empty()) {
1318 if (!m_user_agent.empty()) {
1319 m_response.replace_header(
"Server",m_user_agent);
1321 m_response.remove_header(
"Server");
1327 m_handshake_buffer = m_processor->get_raw(m_response);
1330 m_handshake_buffer = m_response.raw();
1333 if (m_alog.static_test(log::alevel::devel)) {
1334 m_alog.write(log::alevel::devel,
"Raw Handshake response:\n"+m_handshake_buffer);
1335 if (!m_response.get_header(
"Sec-WebSocket-Key3").empty()) {
1336 m_alog.write(log::alevel::devel,
1337 utility::to_hex(m_response.get_header(
"Sec-WebSocket-Key3")));
1342 transport_con_type::async_write(
1343 m_handshake_buffer.data(),
1344 m_handshake_buffer.size(),
1346 &type::handle_write_http_response,
1348 lib::placeholders::_1
1353 template <
typename config>
1354 void connection<config>::handle_write_http_response(lib::error_code
const & ec) {
1355 m_alog.write(log::alevel::devel,
"handle_write_http_response");
1357 lib::error_code ecm = ec;
1360 scoped_lock_type lock(m_connection_state_lock);
1362 if (m_state == session::state::connecting) {
1363 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1364 ecm = error::make_error_code(error::invalid_state);
1366 }
else if (m_state == session::state::closed) {
1370 m_alog.write(log::alevel::devel,
1371 "handle_write_http_response invoked after connection was closed");
1374 ecm = error::make_error_code(error::invalid_state);
1379 if (ecm == transport::error::eof && m_state == session::state::closed) {
1381 m_alog.write(log::alevel::devel,
1382 "got (expected) eof/state error from closed con");
1386 log_err(log::elevel::rerror,
"handle_write_http_response",ecm);
1387 this->terminate(ecm);
1391 if (m_handshake_timer) {
1392 m_handshake_timer->cancel();
1393 m_handshake_timer.reset();
1396 if (m_response.get_status_code() != http::status_code::switching_protocols)
1403 std::stringstream s;
1404 s <<
"Handshake ended with HTTP error: " 1405 << m_response.get_status_code();
1406 m_elog.write(log::elevel::rerror,s.str());
1411 this->log_http_result();
1414 m_alog.write(log::alevel::devel,
1415 "got to writing HTTP results with m_ec set: "+m_ec.message());
1417 m_ec = make_error_code(error::http_connection_ended);
1420 this->terminate(m_ec);
1424 this->log_open_result();
1426 m_internal_state = istate::PROCESS_CONNECTION;
1427 m_state = session::state::open;
1429 if (m_open_handler) {
1430 m_open_handler(m_connection_hdl);
1433 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1436 template <
typename config>
1437 void connection<config>::send_http_request() {
1438 m_alog.write(log::alevel::devel,
"connection send_http_request");
1446 ec = m_processor->client_handshake_request(m_request,m_uri,
1447 m_requested_subprotocols);
1450 log_err(log::elevel::fatal,
"Internal library error: Processor",ec);
1454 m_elog.write(log::elevel::fatal,
"Internal library error: missing processor");
1459 if (m_request.get_header(
"User-Agent").empty()) {
1460 if (!m_user_agent.empty()) {
1461 m_request.replace_header(
"User-Agent",m_user_agent);
1463 m_request.remove_header(
"User-Agent");
1467 m_handshake_buffer = m_request.raw();
1469 if (m_alog.static_test(log::alevel::devel)) {
1470 m_alog.write(log::alevel::devel,
"Raw Handshake request:\n"+m_handshake_buffer);
1473 if (m_open_handshake_timeout_dur > 0) {
1474 m_handshake_timer = transport_con_type::set_timer(
1475 m_open_handshake_timeout_dur,
1477 &type::handle_open_handshake_timeout,
1479 lib::placeholders::_1
1484 transport_con_type::async_write(
1485 m_handshake_buffer.data(),
1486 m_handshake_buffer.size(),
1488 &type::handle_send_http_request,
1490 lib::placeholders::_1
1495 template <
typename config>
1496 void connection<config>::handle_send_http_request(lib::error_code
const & ec) {
1497 m_alog.write(log::alevel::devel,
"handle_send_http_request");
1499 lib::error_code ecm = ec;
1502 scoped_lock_type lock(m_connection_state_lock);
1504 if (m_state == session::state::connecting) {
1505 if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1506 ecm = error::make_error_code(error::invalid_state);
1508 m_internal_state = istate::READ_HTTP_RESPONSE;
1510 }
else if (m_state == session::state::closed) {
1514 m_alog.write(log::alevel::devel,
1515 "handle_send_http_request invoked after connection was closed");
1518 ecm = error::make_error_code(error::invalid_state);
1523 if (ecm == transport::error::eof && m_state == session::state::closed) {
1525 m_alog.write(log::alevel::devel,
1526 "got (expected) eof/state error from closed con");
1530 log_err(log::elevel::rerror,
"handle_send_http_request",ecm);
1531 this->terminate(ecm);
1535 transport_con_type::async_read_at_least(
1538 config::connection_read_buffer_size,
1540 &type::handle_read_http_response,
1542 lib::placeholders::_1,
1543 lib::placeholders::_2
1548 template <
typename config>
1549 void connection<config>::handle_read_http_response(lib::error_code
const & ec,
1550 size_t bytes_transferred)
1552 m_alog.write(log::alevel::devel,
"handle_read_http_response");
1554 lib::error_code ecm = ec;
1557 scoped_lock_type lock(m_connection_state_lock);
1559 if (m_state == session::state::connecting) {
1560 if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1561 ecm = error::make_error_code(error::invalid_state);
1563 }
else if (m_state == session::state::closed) {
1567 m_alog.write(log::alevel::devel,
1568 "handle_read_http_response invoked after connection was closed");
1571 ecm = error::make_error_code(error::invalid_state);
1576 if (ecm == transport::error::eof && m_state == session::state::closed) {
1578 m_alog.write(log::alevel::devel,
1579 "got (expected) eof/state error from closed con");
1583 log_err(log::elevel::rerror,
"handle_read_http_response",ecm);
1584 this->terminate(ecm);
1588 size_t bytes_processed = 0;
1591 bytes_processed = m_response.consume(m_buf,bytes_transferred);
1592 }
catch (http::exception & e) {
1593 m_elog.write(log::elevel::rerror,
1594 std::string(
"error in handle_read_http_response: ")+e.what());
1595 this->terminate(make_error_code(error::general));
1599 m_alog.write(log::alevel::devel,std::string(
"Raw response: ")+m_response.raw());
1601 if (m_response.headers_ready()) {
1602 if (m_handshake_timer) {
1603 m_handshake_timer->cancel();
1604 m_handshake_timer.reset();
1607 lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1612 log_err(log::elevel::rerror,
"Server handshake response",validate_ec);
1613 this->terminate(validate_ec);
1619 std::pair<lib::error_code,std::string> neg_results;
1620 neg_results = m_processor->negotiate_extensions(m_response);
1622 if (neg_results.first) {
1630 m_alog.write(log::alevel::devel,
"Extension negotiation failed: " 1631 + neg_results.first.message());
1632 this->terminate(make_error_code(error::extension_neg_failed));
1637 m_internal_state = istate::PROCESS_CONNECTION;
1638 m_state = session::state::open;
1640 this->log_open_result();
1642 if (m_open_handler) {
1643 m_open_handler(m_connection_hdl);
1649 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1650 m_buf_cursor = bytes_transferred-bytes_processed;
1652 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1654 transport_con_type::async_read_at_least(
1657 config::connection_read_buffer_size,
1659 &type::handle_read_http_response,
1661 lib::placeholders::_1,
1662 lib::placeholders::_2
1668 template <
typename config>
1669 void connection<config>::handle_open_handshake_timeout(
1670 lib::error_code
const & ec)
1672 if (ec == transport::error::operation_aborted) {
1673 m_alog.write(log::alevel::devel,
"open handshake timer cancelled");
1675 m_alog.write(log::alevel::devel,
1676 "open handle_open_handshake_timeout error: "+ec.message());
1679 m_alog.write(log::alevel::devel,
"open handshake timer expired");
1680 terminate(make_error_code(error::open_handshake_timeout));
1684 template <
typename config>
1685 void connection<config>::handle_close_handshake_timeout(
1686 lib::error_code
const & ec)
1688 if (ec == transport::error::operation_aborted) {
1689 m_alog.write(log::alevel::devel,
"asio close handshake timer cancelled");
1691 m_alog.write(log::alevel::devel,
1692 "asio open handle_close_handshake_timeout error: "+ec.message());
1695 m_alog.write(log::alevel::devel,
"asio close handshake timer expired");
1696 terminate(make_error_code(error::close_handshake_timeout));
1700 template <
typename config>
1701 void connection<config>::terminate(lib::error_code
const & ec) {
1702 if (m_alog.static_test(log::alevel::devel)) {
1703 m_alog.write(log::alevel::devel,
"connection terminate");
1707 if (m_handshake_timer) {
1708 m_handshake_timer->cancel();
1709 m_handshake_timer.reset();
1712 terminate_status tstat = unknown;
1715 m_local_close_code = close::status::abnormal_close;
1716 m_local_close_reason = ec.message();
1721 m_http_state = session::http_state::closed;
1723 if (m_state == session::state::connecting) {
1724 m_state = session::state::closed;
1729 if (m_ec != error::http_connection_ended) {
1732 }
else if (m_state != session::state::closed) {
1733 m_state = session::state::closed;
1736 m_alog.write(log::alevel::devel,
1737 "terminate called on connection that was already terminated");
1743 transport_con_type::async_shutdown(
1745 &type::handle_terminate,
1748 lib::placeholders::_1
1753 template <
typename config>
1754 void connection<config>::handle_terminate(terminate_status tstat,
1755 lib::error_code
const & ec)
1757 if (m_alog.static_test(log::alevel::devel)) {
1758 m_alog.write(log::alevel::devel,
"connection handle_terminate");
1763 log_err(log::elevel::devel,
"handle_terminate",ec);
1767 if (tstat == failed) {
1768 if (m_ec != error::http_connection_ended) {
1769 if (m_fail_handler) {
1770 m_fail_handler(m_connection_hdl);
1773 }
else if (tstat == closed) {
1774 if (m_close_handler) {
1775 m_close_handler(m_connection_hdl);
1779 m_elog.write(log::elevel::rerror,
"Unknown terminate_status");
1785 if (m_termination_handler) {
1787 m_termination_handler(type::get_shared());
1788 }
catch (std::exception
const & e) {
1789 m_elog.write(log::elevel::warn,
1790 std::string(
"termination_handler call failed. Reason was: ")+e.what());
1795 template <
typename config>
1796 void connection<config>::write_frame() {
1800 scoped_lock_type lock(m_write_lock);
1812 message_ptr next_message = write_pop();
1813 while (next_message) {
1814 m_current_msgs.push_back(next_message);
1815 if (!next_message->get_terminal()) {
1816 next_message = write_pop();
1818 next_message = message_ptr();
1822 if (m_current_msgs.empty()) {
1829 m_write_flag =
true;
1833 typename std::vector<message_ptr>::iterator it;
1834 for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1835 std::string
const & header = (*it)->get_header();
1836 std::string
const & payload = (*it)->get_payload();
1838 m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
1839 m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
1843 if (m_alog.static_test(log::alevel::frame_header)) {
1844 if (m_alog.dynamic_test(log::alevel::frame_header)) {
1845 std::stringstream general,header,payload;
1847 general <<
"Dispatching write containing " << m_current_msgs.size()
1848 <<
" message(s) containing ";
1849 header <<
"Header Bytes: \n";
1850 payload <<
"Payload Bytes: \n";
1855 for (size_t i = 0; i < m_current_msgs.size(); i++) {
1856 hbytes += m_current_msgs[i]->get_header().size();
1857 pbytes += m_current_msgs[i]->get_payload().size();
1860 header <<
"[" << i <<
"] (" 1861 << m_current_msgs[i]->get_header().size() <<
") " 1862 << utility::to_hex(m_current_msgs[i]->get_header()) <<
"\n";
1864 if (m_alog.static_test(log::alevel::frame_payload)) {
1865 if (m_alog.dynamic_test(log::alevel::frame_payload)) {
1866 payload <<
"[" << i <<
"] (" 1867 << m_current_msgs[i]->get_payload().size() <<
") ["<<m_current_msgs[i]->get_opcode()<<
"] " 1868 << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1869 m_current_msgs[i]->get_payload() :
1870 utility::to_hex(m_current_msgs[i]->get_payload())
1877 general << hbytes <<
" header bytes and " << pbytes <<
" payload bytes";
1879 m_alog.write(log::alevel::frame_header,general.str());
1880 m_alog.write(log::alevel::frame_header,header.str());
1881 m_alog.write(log::alevel::frame_payload,payload.str());
1885 transport_con_type::async_write(
1887 m_write_frame_handler
1891 template <
typename config>
1892 void connection<config>::handle_write_frame(lib::error_code
const & ec)
1894 if (m_alog.static_test(log::alevel::devel)) {
1895 m_alog.write(log::alevel::devel,
"connection handle_write_frame");
1898 bool terminal = m_current_msgs.back()->get_terminal();
1900 m_send_buffer.clear();
1901 m_current_msgs.clear();
1905 log_err(log::elevel::fatal,
"handle_write_frame",ec);
1906 this->terminate(ec);
1911 this->terminate(lib::error_code());
1915 bool needs_writing =
false;
1917 scoped_lock_type lock(m_write_lock);
1920 m_write_flag =
false;
1922 needs_writing = !m_send_queue.empty();
1925 if (needs_writing) {
1926 transport_con_type::dispatch(lib::bind(
1933 template <
typename config>
1934 std::vector<
int>
const & connection<config>::get_supported_versions()
const 1936 return versions_supported;
1939 template <
typename config>
1940 void connection<config>::process_control_frame(
typename config::message_type::ptr msg)
1942 m_alog.write(log::alevel::devel,
"process_control_frame");
1944 frame::opcode::value op = msg->get_opcode();
1947 std::stringstream s;
1948 s <<
"Control frame received with opcode " << op;
1949 m_alog.write(log::alevel::control,s.str());
1951 if (m_state == session::state::closed) {
1952 m_elog.write(log::elevel::warn,
"got frame in state closed");
1955 if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1956 m_elog.write(log::elevel::warn,
"got non-close frame in state closing");
1960 if (op == frame::opcode::PING) {
1961 bool should_reply =
true;
1963 if (m_ping_handler) {
1964 should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1968 this->pong(msg->get_payload(),ec);
1970 log_err(log::elevel::devel,
"Failed to send response pong",ec);
1973 }
else if (op == frame::opcode::PONG) {
1974 if (m_pong_handler) {
1975 m_pong_handler(m_connection_hdl, msg->get_payload());
1978 m_ping_timer->cancel();
1980 }
else if (op == frame::opcode::CLOSE) {
1981 m_alog.write(log::alevel::devel,
"got close frame");
1984 m_remote_close_code = close::extract_code(msg->get_payload(),ec);
1987 if (config::drop_on_protocol_error) {
1988 s <<
"Received invalid close code " << m_remote_close_code
1989 <<
" dropping connection per config.";
1990 m_elog.write(log::elevel::devel,s.str());
1991 this->terminate(ec);
1993 s <<
"Received invalid close code " << m_remote_close_code
1994 <<
" sending acknowledgement and closing";
1995 m_elog.write(log::elevel::devel,s.str());
1996 ec = send_close_ack(close::status::protocol_error,
1997 "Invalid close code");
1999 log_err(log::elevel::devel,
"send_close_ack",ec);
2005 m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
2007 if (config::drop_on_protocol_error) {
2008 m_elog.write(log::elevel::devel,
2009 "Received invalid close reason. Dropping connection per config");
2010 this->terminate(ec);
2012 m_elog.write(log::elevel::devel,
2013 "Received invalid close reason. Sending acknowledgement and closing");
2014 ec = send_close_ack(close::status::protocol_error,
2015 "Invalid close reason");
2017 log_err(log::elevel::devel,
"send_close_ack",ec);
2023 if (m_state == session::state::open) {
2025 s <<
"Received close frame with code " << m_remote_close_code
2026 <<
" and reason " << m_remote_close_reason;
2027 m_alog.write(log::alevel::devel,s.str());
2029 ec = send_close_ack();
2031 log_err(log::elevel::devel,
"send_close_ack",ec);
2033 }
else if (m_state == session::state::closing && !m_was_clean) {
2035 m_alog.write(log::alevel::devel,
"Got acknowledgement of close");
2047 terminate(lib::error_code());
2051 m_elog.write(log::elevel::devel,
"Got close frame in wrong state");
2055 m_elog.write(log::elevel::devel,
"Got control frame with invalid opcode");
2060 template <
typename config>
2061 lib::error_code connection<config>::send_close_ack(close::status::value code,
2062 std::string
const & reason)
2064 return send_close_frame(code,reason,
true,m_is_server);
2067 template <
typename config>
2068 lib::error_code connection<config>::send_close_frame(close::status::value code,
2069 std::string
const & reason,
bool ack,
bool terminal)
2071 m_alog.write(log::alevel::devel,
"send_close_frame");
2081 if (config::silent_close) {
2082 m_alog.write(log::alevel::devel,
"closing silently");
2083 m_local_close_code = close::status::no_status;
2084 m_local_close_reason.clear();
2085 }
else if (code != close::status::blank) {
2086 m_alog.write(log::alevel::devel,
"closing with specified codes");
2087 m_local_close_code = code;
2088 m_local_close_reason = reason;
2090 m_alog.write(log::alevel::devel,
"closing with no status code");
2091 m_local_close_code = close::status::no_status;
2092 m_local_close_reason.clear();
2093 }
else if (m_remote_close_code == close::status::no_status) {
2094 m_alog.write(log::alevel::devel,
2095 "acknowledging a no-status close with normal code");
2096 m_local_close_code = close::status::normal;
2097 m_local_close_reason.clear();
2099 m_alog.write(log::alevel::devel,
"acknowledging with remote codes");
2100 m_local_close_code = m_remote_close_code;
2101 m_local_close_reason = m_remote_close_reason;
2104 std::stringstream s;
2105 s <<
"Closing with code: " << m_local_close_code <<
", and reason: " 2106 << m_local_close_reason;
2107 m_alog.write(log::alevel::devel,s.str());
2109 message_ptr msg = m_msg_manager->get_message();
2111 return error::make_error_code(error::no_outgoing_buffers);
2114 lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2115 m_local_close_reason,msg);
2124 msg->set_terminal(
true);
2127 m_state = session::state::closing;
2135 if (m_close_handshake_timeout_dur > 0) {
2136 m_handshake_timer = transport_con_type::set_timer(
2137 m_close_handshake_timeout_dur,
2139 &type::handle_close_handshake_timeout,
2141 lib::placeholders::_1
2146 bool needs_writing =
false;
2148 scoped_lock_type lock(m_write_lock);
2150 needs_writing = !m_write_flag && !m_send_queue.empty();
2153 if (needs_writing) {
2154 transport_con_type::dispatch(lib::bind(
2160 return lib::error_code();
2163 template <
typename config>
2164 typename connection<config>::processor_ptr
2165 connection<config>::get_processor(
int version)
const {
2172 p = lib::make_shared<processor::hybi00<config> >(
2173 transport_con_type::is_secure(),
2179 p = lib::make_shared<processor::hybi07<config> >(
2180 transport_con_type::is_secure(),
2187 p = lib::make_shared<processor::hybi08<config> >(
2188 transport_con_type::is_secure(),
2195 p = lib::make_shared<processor::hybi13<config> >(
2196 transport_con_type::is_secure(),
2207 p->set_max_message_size(m_max_message_size);
2212 template <
typename config>
2213 void connection<config>::write_push(
typename config::message_type::ptr msg)
2219 m_send_buffer_size += msg->get_payload().size();
2220 m_send_queue.push(msg);
2222 if (m_alog.static_test(log::alevel::devel)) {
2223 std::stringstream s;
2224 s <<
"write_push: message count: " << m_send_queue.size()
2225 <<
" buffer size: " << m_send_buffer_size;
2226 m_alog.write(log::alevel::devel,s.str());
2230 template <
typename config>
2231 typename config::message_type::ptr connection<config>::write_pop()
2235 if (m_send_queue.empty()) {
2239 msg = m_send_queue.front();
2241 m_send_buffer_size -= msg->get_payload().size();
2244 if (m_alog.static_test(log::alevel::devel)) {
2245 std::stringstream s;
2246 s <<
"write_pop: message count: " << m_send_queue.size()
2247 <<
" buffer size: " << m_send_buffer_size;
2248 m_alog.write(log::alevel::devel,s.str());
2253 template <
typename config>
2254 void connection<config>::log_open_result()
2256 std::stringstream s;
2259 if (!processor::is_websocket_handshake(m_request)) {
2262 version = processor::get_websocket_version(m_request);
2266 s << (version == -1 ?
"HTTP" :
"WebSocket") <<
" Connection ";
2269 s << transport_con_type::get_remote_endpoint() <<
" ";
2272 if (version != -1) {
2273 s <<
"v" << version <<
" ";
2277 std::string ua = m_request.get_header(
"User-Agent");
2282 s <<
"\"" << utility::string_replace_all(ua,
"\"",
"\\\"") <<
"\" ";
2286 s << (m_uri ? m_uri->get_resource() :
"NULL") <<
" ";
2289 s << m_response.get_status_code();
2291 m_alog.write(log::alevel::connect,s.str());
2294 template <
typename config>
2295 void connection<config>::log_close_result()
2297 std::stringstream s;
2300 <<
"close local:[" << m_local_close_code
2301 << (m_local_close_reason.empty() ?
"" :
","+m_local_close_reason)
2302 <<
"] remote:[" << m_remote_close_code
2303 << (m_remote_close_reason.empty() ?
"" :
","+m_remote_close_reason) <<
"]";
2305 m_alog.write(log::alevel::disconnect,s.str());
2308 template <
typename config>
2309 void connection<config>::log_fail_result()
2311 std::stringstream s;
2313 int version = processor::get_websocket_version(m_request);
2316 s <<
"WebSocket Connection ";
2319 s << transport_con_type::get_remote_endpoint();
2323 s <<
" v" << version;
2327 std::string ua = m_request.get_header(
"User-Agent");
2332 s <<
" \"" << utility::string_replace_all(ua,
"\"",
"\\\"") <<
"\" ";
2336 s << (m_uri ? m_uri->get_resource() :
"-");
2339 s <<
" " << m_response.get_status_code();
2342 s <<
" " << m_ec <<
" " << m_ec.message();
2344 m_alog.write(log::alevel::fail,s.str());
2347 template <
typename config>
2348 void connection<config>::log_http_result() {
2349 std::stringstream s;
2351 if (processor::is_websocket_handshake(m_request)) {
2352 m_alog.write(log::alevel::devel,
"Call to log_http_result for WebSocket");
2357 s << (m_request.get_header(
"host").empty() ?
"-" : m_request.get_header(
"host"))
2358 <<
" " << transport_con_type::get_remote_endpoint()
2359 <<
" \"" << m_request.get_method()
2360 <<
" " << (m_uri ? m_uri->get_resource() :
"-")
2361 <<
" " << m_request.get_version() <<
"\" " << m_response.get_status_code()
2362 <<
" " << m_response.get_body().size();
2365 std::string ua = m_request.get_header(
"User-Agent");
2370 s <<
" \"" << utility::string_replace_all(ua,
"\"",
"\\\"") <<
"\" ";
2373 m_alog.write(log::alevel::http,s.str());