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::action_after_shutdown) {
1003 echannel = log::elevel::info;
1012 log_err(echannel,
"handle_read_frame", ecm);
1013 this->terminate(ecm);
1026 if (m_alog->static_test(log::alevel::devel)) {
1027 std::stringstream s;
1028 s <<
"p = " << p <<
" bytes transferred = " << bytes_transferred;
1029 m_alog->write(log::alevel::devel,s.str());
1032 while (p < bytes_transferred) {
1033 if (m_alog->static_test(log::alevel::devel)) {
1034 std::stringstream s;
1035 s <<
"calling consume with " << bytes_transferred-p <<
" bytes";
1036 m_alog->write(log::alevel::devel,s.str());
1039 lib::error_code consume_ec;
1041 if (m_alog->static_test(log::alevel::devel)) {
1042 std::stringstream s;
1043 s <<
"Processing Bytes: " << utility::to_hex(
reinterpret_cast<uint8_t*>(m_buf)+p,bytes_transferred-p);
1044 m_alog->write(log::alevel::devel,s.str());
1047 p += m_processor->consume(
1048 reinterpret_cast<uint8_t*>(m_buf)+p,
1049 bytes_transferred-p,
1053 if (m_alog->static_test(log::alevel::devel)) {
1054 std::stringstream s;
1055 s <<
"bytes left after consume: " << bytes_transferred-p;
1056 m_alog->write(log::alevel::devel,s.str());
1059 log_err(log::elevel::rerror,
"consume", consume_ec);
1061 if (config::drop_on_protocol_error) {
1062 this->terminate(consume_ec);
1065 lib::error_code close_ec;
1067 processor::error::to_ws(consume_ec),
1068 consume_ec.message(),
1073 log_err(log::elevel::fatal,
"Protocol error close frame ", close_ec);
1074 this->terminate(close_ec);
1081 if (m_processor->ready()) {
1082 if (m_alog->static_test(log::alevel::devel)) {
1083 std::stringstream s;
1084 s <<
"Complete message received. Dispatching";
1085 m_alog->write(log::alevel::devel,s.str());
1088 message_ptr msg = m_processor->get_message();
1091 m_alog->write(log::alevel::devel,
"null message from m_processor");
1092 }
else if (!is_control(msg->get_opcode())) {
1094 if (m_state != session::state::open) {
1095 m_elog->write(log::elevel::warn,
"got non-close frame while closing");
1096 }
else if (m_message_handler) {
1097 m_message_handler(m_connection_hdl, msg);
1100 process_control_frame(msg);
1109 template <
typename config>
1110 void connection<config>::read_frame() {
1115 transport_con_type::async_read_at_least(
1125 config::connection_read_buffer_size,
1130 template <
typename config>
1131 lib::error_code connection<config>::initialize_processor() {
1132 m_alog->write(log::alevel::devel,
"initialize_processor");
1135 if (!processor::is_websocket_handshake(m_request)) {
1136 return lib::error_code();
1139 int version = processor::get_websocket_version(m_request);
1142 m_alog->write(log::alevel::devel,
"BAD REQUEST: can't determine version");
1143 m_response.set_status(http::status_code::bad_request);
1144 return error::make_error_code(error::invalid_version);
1147 m_processor = get_processor(version);
1151 return lib::error_code();
1156 m_alog->write(log::alevel::devel,
"BAD REQUEST: no processor for version");
1157 m_response.set_status(http::status_code::bad_request);
1159 std::stringstream ss;
1161 std::vector<
int>::const_iterator it;
1162 for (it = versions_supported.begin(); it != versions_supported.end(); it++)
1168 m_response.replace_header(
"Sec-WebSocket-Version",ss.str());
1169 return error::make_error_code(error::unsupported_version);
1172 template <
typename config>
1173 lib::error_code connection<config>::process_handshake_request() {
1174 m_alog->write(log::alevel::devel,
"process handshake request");
1176 if (!processor::is_websocket_handshake(m_request)) {
1178 m_alog->write(log::alevel::devel,
"HTTP REQUEST");
1181 m_uri = processor::get_uri_from_host(
1183 (transport_con_type::is_secure() ?
"https" :
"http")
1186 if (!m_uri->get_valid()) {
1187 m_alog->write(log::alevel::devel,
"Bad request: failed to parse uri");
1188 m_response.set_status(http::status_code::bad_request);
1189 return error::make_error_code(error::invalid_uri);
1192 if (m_http_handler) {
1194 m_http_handler(m_connection_hdl);
1196 if (m_state == session::state::closed) {
1197 return error::make_error_code(error::http_connection_ended);
1200 set_status(http::status_code::upgrade_required);
1201 return error::make_error_code(error::upgrade_required);
1204 return lib::error_code();
1207 lib::error_code ec = m_processor->validate_handshake(m_request);
1212 m_alog->write(log::alevel::devel,
"Bad request " + ec.message());
1213 m_response.set_status(http::status_code::bad_request);
1219 std::pair<lib::error_code,std::string> neg_results;
1220 neg_results = m_processor->negotiate_extensions(m_request);
1222 if (neg_results.first == processor::error::make_error_code(processor::error::extension_parse_error)) {
1225 m_elog->write(log::elevel::info,
"Bad request: " + neg_results.first.message());
1226 m_response.set_status(http::status_code::bad_request);
1227 return neg_results.first;
1228 }
else if (neg_results.first) {
1232 m_elog->write(log::elevel::info,
1233 "Extension negotiation failed: " + neg_results.first.message());
1238 if (neg_results.second.size() > 0) {
1239 m_response.replace_header(
"Sec-WebSocket-Extensions",
1240 neg_results.second);
1245 m_uri = m_processor->get_uri(m_request);
1248 if (!m_uri->get_valid()) {
1249 m_alog->write(log::alevel::devel,
"Bad request: failed to parse uri");
1250 m_response.set_status(http::status_code::bad_request);
1251 return error::make_error_code(error::invalid_uri);
1255 lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1256 m_requested_subprotocols);
1263 if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1264 m_response.set_status(http::status_code::switching_protocols);
1268 ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1271 std::stringstream s;
1272 s <<
"Processing error: " << ec <<
"(" << ec.message() <<
")";
1273 m_alog->write(log::alevel::devel, s.str());
1275 m_response.set_status(http::status_code::internal_server_error);
1280 m_alog->write(log::alevel::devel,
"USER REJECT");
1285 if (m_response.get_status_code() == http::status_code::uninitialized) {
1286 m_response.set_status(http::status_code::bad_request);
1289 return error::make_error_code(error::rejected);
1292 return lib::error_code();
1295 template <
typename config>
1296 void connection<config>::write_http_response(lib::error_code
const & ec) {
1297 m_alog->write(log::alevel::devel,
"connection write_http_response");
1299 if (ec == error::make_error_code(error::http_connection_ended)) {
1300 m_alog->write(log::alevel::http,
"An HTTP handler took over the connection.");
1304 if (m_response.get_status_code() == http::status_code::uninitialized) {
1305 m_response.set_status(http::status_code::internal_server_error);
1306 m_ec = error::make_error_code(error::general);
1311 m_response.set_version(
"HTTP/1.1");
1314 if (m_response.get_header(
"Server").empty()) {
1315 if (!m_user_agent.empty()) {
1316 m_response.replace_header(
"Server",m_user_agent);
1318 m_response.remove_header(
"Server");
1324 m_handshake_buffer = m_processor->get_raw(m_response);
1327 m_handshake_buffer = m_response.raw();
1330 if (m_alog->static_test(log::alevel::devel)) {
1331 m_alog->write(log::alevel::devel,
"Raw Handshake response:\n"+m_handshake_buffer);
1332 if (!m_response.get_header(
"Sec-WebSocket-Key3").empty()) {
1333 m_alog->write(log::alevel::devel,
1334 utility::to_hex(m_response.get_header(
"Sec-WebSocket-Key3")));
1339 transport_con_type::async_write(
1340 m_handshake_buffer.data(),
1341 m_handshake_buffer.size(),
1343 &type::handle_write_http_response,
1345 lib::placeholders::_1
1350 template <
typename config>
1351 void connection<config>::handle_write_http_response(lib::error_code
const & ec) {
1352 m_alog->write(log::alevel::devel,
"handle_write_http_response");
1354 lib::error_code ecm = ec;
1357 scoped_lock_type lock(m_connection_state_lock);
1359 if (m_state == session::state::connecting) {
1360 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1361 ecm = error::make_error_code(error::invalid_state);
1363 }
else if (m_state == session::state::closed) {
1367 m_alog->write(log::alevel::devel,
1368 "handle_write_http_response invoked after connection was closed");
1371 ecm = error::make_error_code(error::invalid_state);
1376 if (ecm == transport::error::eof && m_state == session::state::closed) {
1378 m_alog->write(log::alevel::devel,
1379 "got (expected) eof/state error from closed con");
1383 log_err(log::elevel::rerror,
"handle_write_http_response",ecm);
1384 this->terminate(ecm);
1388 if (m_handshake_timer) {
1389 m_handshake_timer->cancel();
1390 m_handshake_timer.reset();
1393 if (m_response.get_status_code() != http::status_code::switching_protocols)
1400 std::stringstream s;
1401 s <<
"Handshake ended with HTTP error: " 1402 << m_response.get_status_code();
1403 m_elog->write(log::elevel::rerror,s.str());
1408 this->log_http_result();
1411 m_alog->write(log::alevel::devel,
1412 "got to writing HTTP results with m_ec set: "+m_ec.message());
1414 m_ec = make_error_code(error::http_connection_ended);
1417 this->terminate(m_ec);
1421 this->log_open_result();
1423 m_internal_state = istate::PROCESS_CONNECTION;
1424 m_state = session::state::open;
1426 if (m_open_handler) {
1427 m_open_handler(m_connection_hdl);
1430 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1433 template <
typename config>
1434 void connection<config>::send_http_request() {
1435 m_alog->write(log::alevel::devel,
"connection send_http_request");
1443 ec = m_processor->client_handshake_request(m_request,m_uri,
1444 m_requested_subprotocols);
1447 log_err(log::elevel::fatal,
"Internal library error: Processor",ec);
1451 m_elog->write(log::elevel::fatal,
"Internal library error: missing processor");
1456 if (m_request.get_header(
"User-Agent").empty()) {
1457 if (!m_user_agent.empty()) {
1458 m_request.replace_header(
"User-Agent",m_user_agent);
1460 m_request.remove_header(
"User-Agent");
1464 m_handshake_buffer = m_request.raw();
1466 if (m_alog->static_test(log::alevel::devel)) {
1467 m_alog->write(log::alevel::devel,
"Raw Handshake request:\n"+m_handshake_buffer);
1470 if (m_open_handshake_timeout_dur > 0) {
1471 m_handshake_timer = transport_con_type::set_timer(
1472 m_open_handshake_timeout_dur,
1474 &type::handle_open_handshake_timeout,
1476 lib::placeholders::_1
1481 transport_con_type::async_write(
1482 m_handshake_buffer.data(),
1483 m_handshake_buffer.size(),
1485 &type::handle_send_http_request,
1487 lib::placeholders::_1
1492 template <
typename config>
1493 void connection<config>::handle_send_http_request(lib::error_code
const & ec) {
1494 m_alog->write(log::alevel::devel,
"handle_send_http_request");
1496 lib::error_code ecm = ec;
1499 scoped_lock_type lock(m_connection_state_lock);
1501 if (m_state == session::state::connecting) {
1502 if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1503 ecm = error::make_error_code(error::invalid_state);
1505 m_internal_state = istate::READ_HTTP_RESPONSE;
1507 }
else if (m_state == session::state::closed) {
1511 m_alog->write(log::alevel::devel,
1512 "handle_send_http_request invoked after connection was closed");
1515 ecm = error::make_error_code(error::invalid_state);
1520 if (ecm == transport::error::eof && m_state == session::state::closed) {
1522 m_alog->write(log::alevel::devel,
1523 "got (expected) eof/state error from closed con");
1527 log_err(log::elevel::rerror,
"handle_send_http_request",ecm);
1528 this->terminate(ecm);
1532 transport_con_type::async_read_at_least(
1535 config::connection_read_buffer_size,
1537 &type::handle_read_http_response,
1539 lib::placeholders::_1,
1540 lib::placeholders::_2
1545 template <
typename config>
1546 void connection<config>::handle_read_http_response(lib::error_code
const & ec,
1547 size_t bytes_transferred)
1549 m_alog->write(log::alevel::devel,
"handle_read_http_response");
1551 lib::error_code ecm = ec;
1554 scoped_lock_type lock(m_connection_state_lock);
1556 if (m_state == session::state::connecting) {
1557 if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1558 ecm = error::make_error_code(error::invalid_state);
1560 }
else if (m_state == session::state::closed) {
1564 m_alog->write(log::alevel::devel,
1565 "handle_read_http_response invoked after connection was closed");
1568 ecm = error::make_error_code(error::invalid_state);
1573 if (ecm == transport::error::eof && m_state == session::state::closed) {
1575 m_alog->write(log::alevel::devel,
1576 "got (expected) eof/state error from closed con");
1580 log_err(log::elevel::rerror,
"handle_read_http_response",ecm);
1581 this->terminate(ecm);
1585 size_t bytes_processed = 0;
1588 bytes_processed = m_response.consume(m_buf,bytes_transferred);
1589 }
catch (http::exception & e) {
1590 m_elog->write(log::elevel::rerror,
1591 std::string(
"error in handle_read_http_response: ")+e.what());
1592 this->terminate(make_error_code(error::general));
1596 m_alog->write(log::alevel::devel,std::string(
"Raw response: ")+m_response.raw());
1598 if (m_response.headers_ready()) {
1599 if (m_handshake_timer) {
1600 m_handshake_timer->cancel();
1601 m_handshake_timer.reset();
1604 lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1609 log_err(log::elevel::rerror,
"Server handshake response",validate_ec);
1610 this->terminate(validate_ec);
1616 std::pair<lib::error_code,std::string> neg_results;
1617 neg_results = m_processor->negotiate_extensions(m_response);
1619 if (neg_results.first) {
1627 m_alog->write(log::alevel::devel,
"Extension negotiation failed: " 1628 + neg_results.first.message());
1629 this->terminate(make_error_code(error::extension_neg_failed));
1634 m_internal_state = istate::PROCESS_CONNECTION;
1635 m_state = session::state::open;
1637 this->log_open_result();
1639 if (m_open_handler) {
1640 m_open_handler(m_connection_hdl);
1646 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1647 m_buf_cursor = bytes_transferred-bytes_processed;
1649 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1651 transport_con_type::async_read_at_least(
1654 config::connection_read_buffer_size,
1656 &type::handle_read_http_response,
1658 lib::placeholders::_1,
1659 lib::placeholders::_2
1665 template <
typename config>
1666 void connection<config>::handle_open_handshake_timeout(
1667 lib::error_code
const & ec)
1669 if (ec == transport::error::operation_aborted) {
1670 m_alog->write(log::alevel::devel,
"open handshake timer cancelled");
1672 m_alog->write(log::alevel::devel,
1673 "open handle_open_handshake_timeout error: "+ec.message());
1676 m_alog->write(log::alevel::devel,
"open handshake timer expired");
1677 terminate(make_error_code(error::open_handshake_timeout));
1681 template <
typename config>
1682 void connection<config>::handle_close_handshake_timeout(
1683 lib::error_code
const & ec)
1685 if (ec == transport::error::operation_aborted) {
1686 m_alog->write(log::alevel::devel,
"asio close handshake timer cancelled");
1688 m_alog->write(log::alevel::devel,
1689 "asio open handle_close_handshake_timeout error: "+ec.message());
1692 m_alog->write(log::alevel::devel,
"asio close handshake timer expired");
1693 terminate(make_error_code(error::close_handshake_timeout));
1697 template <
typename config>
1698 void connection<config>::terminate(lib::error_code
const & ec) {
1699 if (m_alog->static_test(log::alevel::devel)) {
1700 m_alog->write(log::alevel::devel,
"connection terminate");
1704 if (m_handshake_timer) {
1705 m_handshake_timer->cancel();
1706 m_handshake_timer.reset();
1709 terminate_status tstat = unknown;
1712 m_local_close_code = close::status::abnormal_close;
1713 m_local_close_reason = ec.message();
1718 m_http_state = session::http_state::closed;
1720 if (m_state == session::state::connecting) {
1721 m_state = session::state::closed;
1726 if (m_ec != error::http_connection_ended) {
1729 }
else if (m_state != session::state::closed) {
1730 m_state = session::state::closed;
1733 m_alog->write(log::alevel::devel,
1734 "terminate called on connection that was already terminated");
1740 transport_con_type::async_shutdown(
1742 &type::handle_terminate,
1745 lib::placeholders::_1
1750 template <
typename config>
1751 void connection<config>::handle_terminate(terminate_status tstat,
1752 lib::error_code
const & ec)
1754 if (m_alog->static_test(log::alevel::devel)) {
1755 m_alog->write(log::alevel::devel,
"connection handle_terminate");
1760 log_err(log::elevel::devel,
"handle_terminate",ec);
1764 if (tstat == failed) {
1765 if (m_ec != error::http_connection_ended) {
1766 if (m_fail_handler) {
1767 m_fail_handler(m_connection_hdl);
1770 }
else if (tstat == closed) {
1771 if (m_close_handler) {
1772 m_close_handler(m_connection_hdl);
1776 m_elog->write(log::elevel::rerror,
"Unknown terminate_status");
1782 if (m_termination_handler) {
1784 m_termination_handler(type::get_shared());
1785 }
catch (std::exception
const & e) {
1786 m_elog->write(log::elevel::warn,
1787 std::string(
"termination_handler call failed. Reason was: ")+e.what());
1792 template <
typename config>
1793 void connection<config>::write_frame() {
1797 scoped_lock_type lock(m_write_lock);
1809 message_ptr next_message = write_pop();
1810 while (next_message) {
1811 m_current_msgs.push_back(next_message);
1812 if (!next_message->get_terminal()) {
1813 next_message = write_pop();
1815 next_message = message_ptr();
1819 if (m_current_msgs.empty()) {
1826 m_write_flag =
true;
1830 typename std::vector<message_ptr>::iterator it;
1831 for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1832 std::string
const & header = (*it)->get_header();
1833 std::string
const & payload = (*it)->get_payload();
1835 m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
1836 m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
1840 if (m_alog->static_test(log::alevel::frame_header)) {
1841 if (m_alog->dynamic_test(log::alevel::frame_header)) {
1842 std::stringstream general,header,payload;
1844 general <<
"Dispatching write containing " << m_current_msgs.size()
1845 <<
" message(s) containing ";
1846 header <<
"Header Bytes: \n";
1847 payload <<
"Payload Bytes: \n";
1852 for (size_t i = 0; i < m_current_msgs.size(); i++) {
1853 hbytes += m_current_msgs[i]->get_header().size();
1854 pbytes += m_current_msgs[i]->get_payload().size();
1857 header <<
"[" << i <<
"] (" 1858 << m_current_msgs[i]->get_header().size() <<
") " 1859 << utility::to_hex(m_current_msgs[i]->get_header()) <<
"\n";
1861 if (m_alog->static_test(log::alevel::frame_payload)) {
1862 if (m_alog->dynamic_test(log::alevel::frame_payload)) {
1863 payload <<
"[" << i <<
"] (" 1864 << m_current_msgs[i]->get_payload().size() <<
") ["<<m_current_msgs[i]->get_opcode()<<
"] " 1865 << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1866 m_current_msgs[i]->get_payload() :
1867 utility::to_hex(m_current_msgs[i]->get_payload())
1874 general << hbytes <<
" header bytes and " << pbytes <<
" payload bytes";
1876 m_alog->write(log::alevel::frame_header,general.str());
1877 m_alog->write(log::alevel::frame_header,header.str());
1878 m_alog->write(log::alevel::frame_payload,payload.str());
1882 transport_con_type::async_write(
1884 m_write_frame_handler
1888 template <
typename config>
1889 void connection<config>::handle_write_frame(lib::error_code
const & ec)
1891 if (m_alog->static_test(log::alevel::devel)) {
1892 m_alog->write(log::alevel::devel,
"connection handle_write_frame");
1895 bool terminal = m_current_msgs.back()->get_terminal();
1897 m_send_buffer.clear();
1898 m_current_msgs.clear();
1902 log_err(log::elevel::fatal,
"handle_write_frame",ec);
1903 this->terminate(ec);
1908 this->terminate(lib::error_code());
1912 bool needs_writing =
false;
1914 scoped_lock_type lock(m_write_lock);
1917 m_write_flag =
false;
1919 needs_writing = !m_send_queue.empty();
1922 if (needs_writing) {
1923 transport_con_type::dispatch(lib::bind(
1930 template <
typename config>
1931 std::vector<
int>
const & connection<config>::get_supported_versions()
const 1933 return versions_supported;
1936 template <
typename config>
1937 void connection<config>::process_control_frame(
typename config::message_type::ptr msg)
1939 m_alog->write(log::alevel::devel,
"process_control_frame");
1941 frame::opcode::value op = msg->get_opcode();
1944 std::stringstream s;
1945 s <<
"Control frame received with opcode " << op;
1946 m_alog->write(log::alevel::control,s.str());
1948 if (m_state == session::state::closed) {
1949 m_elog->write(log::elevel::warn,
"got frame in state closed");
1952 if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1953 m_elog->write(log::elevel::warn,
"got non-close frame in state closing");
1957 if (op == frame::opcode::PING) {
1958 bool should_reply =
true;
1960 if (m_ping_handler) {
1961 should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1965 this->pong(msg->get_payload(),ec);
1967 log_err(log::elevel::devel,
"Failed to send response pong",ec);
1970 }
else if (op == frame::opcode::PONG) {
1971 if (m_pong_handler) {
1972 m_pong_handler(m_connection_hdl, msg->get_payload());
1975 m_ping_timer->cancel();
1977 }
else if (op == frame::opcode::CLOSE) {
1978 m_alog->write(log::alevel::devel,
"got close frame");
1981 m_remote_close_code = close::extract_code(msg->get_payload(),ec);
1984 if (config::drop_on_protocol_error) {
1985 s <<
"Received invalid close code " << m_remote_close_code
1986 <<
" dropping connection per config.";
1987 m_elog->write(log::elevel::devel,s.str());
1988 this->terminate(ec);
1990 s <<
"Received invalid close code " << m_remote_close_code
1991 <<
" sending acknowledgement and closing";
1992 m_elog->write(log::elevel::devel,s.str());
1993 ec = send_close_ack(close::status::protocol_error,
1994 "Invalid close code");
1996 log_err(log::elevel::devel,
"send_close_ack",ec);
2002 m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
2004 if (config::drop_on_protocol_error) {
2005 m_elog->write(log::elevel::devel,
2006 "Received invalid close reason. Dropping connection per config");
2007 this->terminate(ec);
2009 m_elog->write(log::elevel::devel,
2010 "Received invalid close reason. Sending acknowledgement and closing");
2011 ec = send_close_ack(close::status::protocol_error,
2012 "Invalid close reason");
2014 log_err(log::elevel::devel,
"send_close_ack",ec);
2020 if (m_state == session::state::open) {
2022 s <<
"Received close frame with code " << m_remote_close_code
2023 <<
" and reason " << m_remote_close_reason;
2024 m_alog->write(log::alevel::devel,s.str());
2026 ec = send_close_ack();
2028 log_err(log::elevel::devel,
"send_close_ack",ec);
2030 }
else if (m_state == session::state::closing && !m_was_clean) {
2032 m_alog->write(log::alevel::devel,
"Got acknowledgement of close");
2044 terminate(lib::error_code());
2048 m_elog->write(log::elevel::devel,
"Got close frame in wrong state");
2052 m_elog->write(log::elevel::devel,
"Got control frame with invalid opcode");
2057 template <
typename config>
2058 lib::error_code connection<config>::send_close_ack(close::status::value code,
2059 std::string
const & reason)
2061 return send_close_frame(code,reason,
true,m_is_server);
2064 template <
typename config>
2065 lib::error_code connection<config>::send_close_frame(close::status::value code,
2066 std::string
const & reason,
bool ack,
bool terminal)
2068 m_alog->write(log::alevel::devel,
"send_close_frame");
2078 if (config::silent_close) {
2079 m_alog->write(log::alevel::devel,
"closing silently");
2080 m_local_close_code = close::status::no_status;
2081 m_local_close_reason.clear();
2082 }
else if (code != close::status::blank) {
2083 m_alog->write(log::alevel::devel,
"closing with specified codes");
2084 m_local_close_code = code;
2085 m_local_close_reason = reason;
2087 m_alog->write(log::alevel::devel,
"closing with no status code");
2088 m_local_close_code = close::status::no_status;
2089 m_local_close_reason.clear();
2090 }
else if (m_remote_close_code == close::status::no_status) {
2091 m_alog->write(log::alevel::devel,
2092 "acknowledging a no-status close with normal code");
2093 m_local_close_code = close::status::normal;
2094 m_local_close_reason.clear();
2096 m_alog->write(log::alevel::devel,
"acknowledging with remote codes");
2097 m_local_close_code = m_remote_close_code;
2098 m_local_close_reason = m_remote_close_reason;
2101 std::stringstream s;
2102 s <<
"Closing with code: " << m_local_close_code <<
", and reason: " 2103 << m_local_close_reason;
2104 m_alog->write(log::alevel::devel,s.str());
2106 message_ptr msg = m_msg_manager->get_message();
2108 return error::make_error_code(error::no_outgoing_buffers);
2111 lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2112 m_local_close_reason,msg);
2121 msg->set_terminal(
true);
2124 m_state = session::state::closing;
2132 if (m_close_handshake_timeout_dur > 0) {
2133 m_handshake_timer = transport_con_type::set_timer(
2134 m_close_handshake_timeout_dur,
2136 &type::handle_close_handshake_timeout,
2138 lib::placeholders::_1
2143 bool needs_writing =
false;
2145 scoped_lock_type lock(m_write_lock);
2147 needs_writing = !m_write_flag && !m_send_queue.empty();
2150 if (needs_writing) {
2151 transport_con_type::dispatch(lib::bind(
2157 return lib::error_code();
2160 template <
typename config>
2161 typename connection<config>::processor_ptr
2162 connection<config>::get_processor(
int version)
const {
2169 p = lib::make_shared<processor::hybi00<config> >(
2170 transport_con_type::is_secure(),
2176 p = lib::make_shared<processor::hybi07<config> >(
2177 transport_con_type::is_secure(),
2184 p = lib::make_shared<processor::hybi08<config> >(
2185 transport_con_type::is_secure(),
2192 p = lib::make_shared<processor::hybi13<config> >(
2193 transport_con_type::is_secure(),
2204 p->set_max_message_size(m_max_message_size);
2209 template <
typename config>
2210 void connection<config>::write_push(
typename config::message_type::ptr msg)
2216 m_send_buffer_size += msg->get_payload().size();
2217 m_send_queue.push(msg);
2219 if (m_alog->static_test(log::alevel::devel)) {
2220 std::stringstream s;
2221 s <<
"write_push: message count: " << m_send_queue.size()
2222 <<
" buffer size: " << m_send_buffer_size;
2223 m_alog->write(log::alevel::devel,s.str());
2227 template <
typename config>
2228 typename config::message_type::ptr connection<config>::write_pop()
2232 if (m_send_queue.empty()) {
2236 msg = m_send_queue.front();
2238 m_send_buffer_size -= msg->get_payload().size();
2241 if (m_alog->static_test(log::alevel::devel)) {
2242 std::stringstream s;
2243 s <<
"write_pop: message count: " << m_send_queue.size()
2244 <<
" buffer size: " << m_send_buffer_size;
2245 m_alog->write(log::alevel::devel,s.str());
2250 template <
typename config>
2251 void connection<config>::log_open_result()
2253 std::stringstream s;
2256 if (!processor::is_websocket_handshake(m_request)) {
2259 version = processor::get_websocket_version(m_request);
2263 s << (version == -1 ?
"HTTP" :
"WebSocket") <<
" Connection ";
2266 s << transport_con_type::get_remote_endpoint() <<
" ";
2269 if (version != -1) {
2270 s <<
"v" << version <<
" ";
2274 std::string ua = m_request.get_header(
"User-Agent");
2279 s <<
"\"" << utility::string_replace_all(ua,
"\"",
"\\\"") <<
"\" ";
2283 s << (m_uri ? m_uri->get_resource() :
"NULL") <<
" ";
2286 s << m_response.get_status_code();
2288 m_alog->write(log::alevel::connect,s.str());
2291 template <
typename config>
2292 void connection<config>::log_close_result()
2294 std::stringstream s;
2297 <<
"close local:[" << m_local_close_code
2298 << (m_local_close_reason.empty() ?
"" :
","+m_local_close_reason)
2299 <<
"] remote:[" << m_remote_close_code
2300 << (m_remote_close_reason.empty() ?
"" :
","+m_remote_close_reason) <<
"]";
2302 m_alog->write(log::alevel::disconnect,s.str());
2305 template <
typename config>
2306 void connection<config>::log_fail_result()
2308 std::stringstream s;
2310 int version = processor::get_websocket_version(m_request);
2313 s <<
"WebSocket Connection ";
2316 s << transport_con_type::get_remote_endpoint();
2320 s <<
" v" << version;
2324 std::string ua = m_request.get_header(
"User-Agent");
2329 s <<
" \"" << utility::string_replace_all(ua,
"\"",
"\\\"") <<
"\" ";
2333 s << (m_uri ? m_uri->get_resource() :
"-");
2336 s <<
" " << m_response.get_status_code();
2339 s <<
" " << m_ec <<
" " << m_ec.message();
2341 m_alog->write(log::alevel::fail,s.str());
2344 template <
typename config>
2345 void connection<config>::log_http_result() {
2346 std::stringstream s;
2348 if (processor::is_websocket_handshake(m_request)) {
2349 m_alog->write(log::alevel::devel,
"Call to log_http_result for WebSocket");
2354 s << (m_request.get_header(
"host").empty() ?
"-" : m_request.get_header(
"host"))
2355 <<
" " << transport_con_type::get_remote_endpoint()
2356 <<
" \"" << m_request.get_method()
2357 <<
" " << (m_uri ? m_uri->get_resource() :
"-")
2358 <<
" " << m_request.get_version() <<
"\" " << m_response.get_status_code()
2359 <<
" " << m_response.get_body().size();
2362 std::string ua = m_request.get_header(
"User-Agent");
2367 s <<
" \"" << utility::string_replace_all(ua,
"\"",
"\\\"") <<
"\" ";
2370 m_alog->write(log::alevel::http,s.str());