NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.3: NDN, CCN, CCNx, content centric networks
API Documentation
connection_impl.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the WebSocket++ Project nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP
29 #define WEBSOCKETPP_CONNECTION_IMPL_HPP
30 
35 
37 
40 
41 #include <algorithm>
42 #include <exception>
43 #include <sstream>
44 #include <string>
45 #include <utility>
46 #include <vector>
47 
48 namespace websocketpp {
49 
50 namespace istate = session::internal_state;
51 
52 template <typename config>
54  termination_handler new_handler)
55 {
56  m_alog.write(log::alevel::devel,
57  "connection set_termination_handler");
58 
59  //scoped_lock_type lock(m_connection_state_lock);
60 
61  m_termination_handler = new_handler;
62 }
63 
64 template <typename config>
65 std::string const & connection<config>::get_origin() const {
66  //scoped_lock_type lock(m_connection_state_lock);
67  return m_processor->get_origin(m_request);
68 }
69 
70 template <typename config>
72  //scoped_lock_type lock(m_connection_state_lock);
73  return m_send_buffer_size;
74 }
75 
76 template <typename config>
78  //scoped_lock_type lock(m_connection_state_lock);
79  return m_state;
80 }
81 
82 template <typename config>
83 lib::error_code connection<config>::send(std::string const & payload,
85 {
86  message_ptr msg = m_msg_manager->get_message(op,payload.size());
87  msg->append_payload(payload);
88  msg->set_compressed(true);
89 
90  return send(msg);
91 }
92 
93 template <typename config>
94 lib::error_code connection<config>::send(void const * payload, size_t len,
96 {
97  message_ptr msg = m_msg_manager->get_message(op,len);
98  msg->append_payload(payload,len);
99 
100  return send(msg);
101 }
102 
103 template <typename config>
105 {
106  if (m_alog.static_test(log::alevel::devel)) {
107  m_alog.write(log::alevel::devel,"connection send");
108  }
109 
110  {
111  scoped_lock_type lock(m_connection_state_lock);
112  if (m_state != session::state::open) {
114  }
115  }
116 
117  message_ptr outgoing_msg;
118  bool needs_writing = false;
119 
120  if (msg->get_prepared()) {
121  outgoing_msg = msg;
122 
123  scoped_lock_type lock(m_write_lock);
124  write_push(outgoing_msg);
125  needs_writing = !m_write_flag && !m_send_queue.empty();
126  } else {
127  outgoing_msg = m_msg_manager->get_message();
128 
129  if (!outgoing_msg) {
131  }
132 
133  scoped_lock_type lock(m_write_lock);
134  lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg);
135 
136  if (ec) {
137  return ec;
138  }
139 
140  write_push(outgoing_msg);
141  needs_writing = !m_write_flag && !m_send_queue.empty();
142  }
143 
144  if (needs_writing) {
145  transport_con_type::dispatch(lib::bind(
146  &type::write_frame,
147  type::get_shared()
148  ));
149  }
150 
151  return lib::error_code();
152 }
153 
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");
158  }
159 
160  {
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());
167  return;
168  }
169  }
170 
171  message_ptr msg = m_msg_manager->get_message();
172  if (!msg) {
174  return;
175  }
176 
177  ec = m_processor->prepare_ping(payload,msg);
178  if (ec) {return;}
179 
180  // set ping timer if we are listening for one
181  if (m_pong_timeout_handler) {
182  // Cancel any existing timers
183  if (m_ping_timer) {
184  m_ping_timer->cancel();
185  }
186 
187  if (m_pong_timeout_dur > 0) {
188  m_ping_timer = transport_con_type::set_timer(
189  m_pong_timeout_dur,
190  lib::bind(
191  &type::handle_pong_timeout,
192  type::get_shared(),
193  payload,
194  lib::placeholders::_1
195  )
196  );
197  }
198 
199  if (!m_ping_timer) {
200  // Our transport doesn't support timers
201  m_elog.write(log::elevel::warn,"Warning: a pong_timeout_handler is \
202  set but the transport in use does not support timeouts.");
203  }
204  }
205 
206  bool needs_writing = false;
207  {
208  scoped_lock_type lock(m_write_lock);
209  write_push(msg);
210  needs_writing = !m_write_flag && !m_send_queue.empty();
211  }
212 
213  if (needs_writing) {
214  transport_con_type::dispatch(lib::bind(
215  &type::write_frame,
216  type::get_shared()
217  ));
218  }
219 
220  ec = lib::error_code();
221 }
222 
223 template<typename config>
224 void connection<config>::ping(std::string const & payload) {
225  lib::error_code ec;
226  ping(payload,ec);
227  if (ec) {
228  throw exception(ec);
229  }
230 }
231 
232 template<typename config>
234  lib::error_code const & ec)
235 {
236  if (ec) {
238  // ignore, this is expected
239  return;
240  }
241 
242  m_elog.write(log::elevel::devel,"pong_timeout error: "+ec.message());
243  return;
244  }
245 
246  if (m_pong_timeout_handler) {
247  m_pong_timeout_handler(m_connection_hdl,payload);
248  }
249 }
250 
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");
255  }
256 
257  {
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());
264  return;
265  }
266  }
267 
268  message_ptr msg = m_msg_manager->get_message();
269  if (!msg) {
271  return;
272  }
273 
274  ec = m_processor->prepare_pong(payload,msg);
275  if (ec) {return;}
276 
277  bool needs_writing = false;
278  {
279  scoped_lock_type lock(m_write_lock);
280  write_push(msg);
281  needs_writing = !m_write_flag && !m_send_queue.empty();
282  }
283 
284  if (needs_writing) {
285  transport_con_type::dispatch(lib::bind(
286  &type::write_frame,
287  type::get_shared()
288  ));
289  }
290 
291  ec = lib::error_code();
292 }
293 
294 template<typename config>
295 void connection<config>::pong(std::string const & payload) {
296  lib::error_code ec;
297  pong(payload,ec);
298  if (ec) {
299  throw exception(ec);
300  }
301 }
302 
303 template <typename config>
305  std::string const & reason, lib::error_code & ec)
306 {
307  if (m_alog.static_test(log::alevel::devel)) {
308  m_alog.write(log::alevel::devel,"connection close");
309  }
310 
311  // Truncate reason to maximum size allowable in a close frame.
312  std::string tr(reason,0,std::min<size_t>(reason.size(),
314 
315  scoped_lock_type lock(m_connection_state_lock);
316 
317  if (m_state != session::state::open) {
319  return;
320  }
321 
322  ec = this->send_close_frame(code,tr,false,close::status::terminal(code));
323 }
324 
325 template<typename config>
327  std::string const & reason)
328 {
329  lib::error_code ec;
330  close(code,reason,ec);
331  if (ec) {
332  throw exception(ec);
333  }
334 }
335 
337 
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(
344  lib::bind(
345  &type::handle_interrupt,
346  type::get_shared()
347  )
348  );
349 }
350 
351 
352 template <typename config>
354  if (m_interrupt_handler) {
355  m_interrupt_handler(m_connection_hdl);
356  }
357 }
358 
359 template <typename config>
361  m_alog.write(log::alevel::devel,"connection connection::pause_reading");
362  return transport_con_type::dispatch(
363  lib::bind(
364  &type::handle_pause_reading,
365  type::get_shared()
366  )
367  );
368 }
369 
371 template <typename config>
373  m_alog.write(log::alevel::devel,"connection connection::handle_pause_reading");
374  m_read_flag = false;
375 }
376 
377 template <typename config>
379  m_alog.write(log::alevel::devel,"connection connection::resume_reading");
380  return transport_con_type::dispatch(
381  lib::bind(
382  &type::handle_resume_reading,
383  type::get_shared()
384  )
385  );
386 }
387 
389 template <typename config>
391  m_read_flag = true;
392  read_frame();
393 }
394 
395 
396 
397 
398 
399 
400 
401 
402 
403 
404 
405 template <typename config>
407  //scoped_lock_type lock(m_connection_state_lock);
408  return m_uri->get_secure();
409 }
410 
411 template <typename config>
412 std::string const & connection<config>::get_host() const {
413  //scoped_lock_type lock(m_connection_state_lock);
414  return m_uri->get_host();
415 }
416 
417 template <typename config>
418 std::string const & connection<config>::get_resource() const {
419  //scoped_lock_type lock(m_connection_state_lock);
420  return m_uri->get_resource();
421 }
422 
423 template <typename config>
425  //scoped_lock_type lock(m_connection_state_lock);
426  return m_uri->get_port();
427 }
428 
429 template <typename config>
431  //scoped_lock_type lock(m_connection_state_lock);
432  return m_uri;
433 }
434 
435 template <typename config>
437  //scoped_lock_type lock(m_connection_state_lock);
438  m_uri = uri;
439 }
440 
441 
442 
443 
444 
445 
446 template <typename config>
447 std::string const & connection<config>::get_subprotocol() const {
448  return m_subprotocol;
449 }
450 
451 template <typename config>
452 std::vector<std::string> const &
454  return m_requested_subprotocols;
455 }
456 
457 template <typename config>
459  lib::error_code & ec)
460 {
461  if (m_is_server) {
463  return;
464  }
465 
466  // If the value is empty or has a non-RFC2616 token character it is invalid.
467  if (value.empty() || std::find_if(value.begin(),value.end(),
468  http::is_not_token_char) != value.end())
469  {
471  return;
472  }
473 
474  m_requested_subprotocols.push_back(value);
475 }
476 
477 template <typename config>
478 void connection<config>::add_subprotocol(std::string const & value) {
479  lib::error_code ec;
480  this->add_subprotocol(value,ec);
481  if (ec) {
482  throw exception(ec);
483  }
484 }
485 
486 
487 template <typename config>
489  lib::error_code & ec)
490 {
491  if (!m_is_server) {
493  return;
494  }
495 
496  if (value.empty()) {
497  ec = lib::error_code();
498  return;
499  }
500 
502 
503  it = std::find(m_requested_subprotocols.begin(),
504  m_requested_subprotocols.end(),
505  value);
506 
507  if (it == m_requested_subprotocols.end()) {
509  return;
510  }
511 
512  m_subprotocol = value;
513 }
514 
515 template <typename config>
517  lib::error_code ec;
518  this->select_subprotocol(value,ec);
519  if (ec) {
520  throw exception(ec);
521  }
522 }
523 
524 
525 template <typename config>
526 std::string const &
527 connection<config>::get_request_header(std::string const & key) const {
528  return m_request.get_header(key);
529 }
530 
531 template <typename config>
532 std::string const &
534  return m_request.get_body();
535 }
536 
537 template <typename config>
538 std::string const &
539 connection<config>::get_response_header(std::string const & key) const {
540  return m_response.get_header(key);
541 }
542 
543 // TODO: EXCEPTION_FREE
544 template <typename config>
546 {
547  if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
548  throw exception("Call to set_status from invalid state",
550  }
551  m_response.set_status(code);
552 }
553 
554 // TODO: EXCEPTION_FREE
555 template <typename config>
557  std::string const & msg)
558 {
559  if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
560  throw exception("Call to set_status from invalid state",
562  }
563 
564  m_response.set_status(code,msg);
565 }
566 
567 // TODO: EXCEPTION_FREE
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",
573  }
574 
575  m_response.set_body(value);
576 }
577 
578 // TODO: EXCEPTION_FREE
579 template <typename config>
580 void connection<config>::append_header(std::string const & key,
581  std::string const & val)
582 {
583  if (m_is_server) {
584  if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
585  // we are setting response headers for an incoming server connection
586  m_response.append_header(key,val);
587  } else {
588  throw exception("Call to append_header from invalid state",
590  }
591  } else {
592  if (m_internal_state == istate::USER_INIT) {
593  // we are setting initial headers for an outgoing client connection
594  m_request.append_header(key,val);
595  } else {
596  throw exception("Call to append_header from invalid state",
598  }
599  }
600 }
601 
602 // TODO: EXCEPTION_FREE
603 template <typename config>
604 void connection<config>::replace_header(std::string const & key,
605  std::string const & val)
606 {
607  if (m_is_server) {
608  if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
609  // we are setting response headers for an incoming server connection
610  m_response.replace_header(key,val);
611  } else {
612  throw exception("Call to replace_header from invalid state",
614  }
615  } else {
616  if (m_internal_state == istate::USER_INIT) {
617  // we are setting initial headers for an outgoing client connection
618  m_request.replace_header(key,val);
619  } else {
620  throw exception("Call to replace_header from invalid state",
622  }
623  }
624 }
625 
626 // TODO: EXCEPTION_FREE
627 template <typename config>
628 void connection<config>::remove_header(std::string const & key)
629 {
630  if (m_is_server) {
631  if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
632  // we are setting response headers for an incoming server connection
633  m_response.remove_header(key);
634  } else {
635  throw exception("Call to remove_header from invalid state",
637  }
638  } else {
639  if (m_internal_state == istate::USER_INIT) {
640  // we are setting initial headers for an outgoing client connection
641  m_request.remove_header(key);
642  } else {
643  throw exception("Call to remove_header from invalid state",
645  }
646  }
647 }
648 
650 
660 template <typename config>
662  // Cancel handshake timer, otherwise the connection will time out and we'll
663  // close the connection before the app has a chance to send a response.
664  if (m_handshake_timer) {
665  m_handshake_timer->cancel();
666  m_handshake_timer.reset();
667  }
668 
669  // Do something to signal deferral
670  m_http_state = session::http_state::deferred;
671 
672  return lib::error_code();
673 }
674 
676 
685 template <typename config>
686 void connection<config>::send_http_response(lib::error_code & ec) {
687  {
688  scoped_lock_type lock(m_connection_state_lock);
689  if (m_http_state != session::http_state::deferred) {
691  return;
692  }
693 
694  m_http_state = session::http_state::body_written;
695  }
696 
697  this->write_http_response(lib::error_code());
698  ec = lib::error_code();
699 }
700 
701 template <typename config>
703  lib::error_code ec;
704  this->send_http_response(ec);
705  if (ec) {
706  throw exception(ec);
707  }
708 }
709 
710 
711 
712 
713 /******** logic thread ********/
714 
715 template <typename config>
717  m_alog.write(log::alevel::devel,"connection start");
718 
719  if (m_internal_state != istate::USER_INIT) {
720  m_alog.write(log::alevel::devel,"Start called in invalid state");
722  return;
723  }
724 
725  m_internal_state = istate::TRANSPORT_INIT;
726 
727  // Depending on how the transport implements init this function may return
728  // immediately and call handle_transport_init later or call
729  // handle_transport_init from this function.
731  lib::bind(
732  &type::handle_transport_init,
733  type::get_shared(),
734  lib::placeholders::_1
735  )
736  );
737 }
738 
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");
742 
743  lib::error_code ecm = ec;
744 
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");
749  }
750 
751  if (ecm) {
752  std::stringstream s;
753  s << "handle_transport_init received error: "<< ecm.message();
754  m_elog.write(log::elevel::rerror,s.str());
755 
756  this->terminate(ecm);
757  return;
758  }
759 
760  // At this point the transport is ready to read and write bytes.
761  if (m_is_server) {
762  m_internal_state = istate::READ_HTTP_REQUEST;
763  this->read_handshake(1);
764  } else {
765  // We are a client. Set the processor to the version specified in the
766  // config file and send a handshake request.
767  m_internal_state = istate::WRITE_HTTP_REQUEST;
768  m_processor = get_processor(config::client_version);
769  this->send_http_request();
770  }
771 }
772 
773 template <typename config>
774 void connection<config>::read_handshake(size_t num_bytes) {
775  m_alog.write(log::alevel::devel,"connection read_handshake");
776 
777  if (m_open_handshake_timeout_dur > 0) {
778  m_handshake_timer = transport_con_type::set_timer(
779  m_open_handshake_timeout_dur,
780  lib::bind(
781  &type::handle_open_handshake_timeout,
782  type::get_shared(),
783  lib::placeholders::_1
784  )
785  );
786  }
787 
788  transport_con_type::async_read_at_least(
789  num_bytes,
790  m_buf,
792  lib::bind(
793  &type::handle_read_handshake,
794  type::get_shared(),
795  lib::placeholders::_1,
796  lib::placeholders::_2
797  )
798  );
799 }
800 
801 // All exit paths for this function need to call write_http_response() or submit
802 // a new read request with this function as the handler.
803 template <typename config>
804 void connection<config>::handle_read_handshake(lib::error_code const & ec,
805  size_t bytes_transferred)
806 {
807  m_alog.write(log::alevel::devel,"connection handle_read_handshake");
808 
809  lib::error_code ecm = ec;
810 
811  if (!ecm) {
812  scoped_lock_type lock(m_connection_state_lock);
813 
814  if (m_state == session::state::connecting) {
815  if (m_internal_state != istate::READ_HTTP_REQUEST) {
817  }
818  } else if (m_state == session::state::closed) {
819  // The connection was canceled while the response was being sent,
820  // usually by the handshake timer. This is basically expected
821  // (though hopefully rare) and there is nothing we can do so ignore.
822  m_alog.write(log::alevel::devel,
823  "handle_read_handshake invoked after connection was closed");
824  return;
825  } else {
827  }
828  }
829 
830  if (ecm) {
831  if (ecm == transport::error::eof && m_state == session::state::closed) {
832  // we expect to get eof if the connection is closed already
833  m_alog.write(log::alevel::devel,
834  "got (expected) eof/state error from closed con");
835  return;
836  }
837 
838  log_err(log::elevel::rerror,"handle_read_handshake",ecm);
839  this->terminate(ecm);
840  return;
841  }
842 
843  // Boundaries checking. TODO: How much of this should be done?
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));
847  return;
848  }
849 
850  size_t bytes_processed = 0;
851  try {
852  bytes_processed = m_request.consume(m_buf,bytes_transferred);
853  } catch (http::exception &e) {
854  // All HTTP exceptions will result in this request failing and an error
855  // response being returned. No more bytes will be read in this con.
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));
858  return;
859  }
860 
861  // More paranoid boundaries checking.
862  // TODO: Is this overkill?
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));
866  return;
867  }
868 
869  if (m_alog.static_test(log::alevel::devel)) {
870  std::stringstream s;
871  s << "bytes_transferred: " << bytes_transferred
872  << " bytes, bytes processed: " << bytes_processed << " bytes";
873  m_alog.write(log::alevel::devel,s.str());
874  }
875 
876  if (m_request.ready()) {
877  lib::error_code processor_ec = this->initialize_processor();
878  if (processor_ec) {
879  this->write_http_response_error(processor_ec);
880  return;
881  }
882 
883  if (m_processor && m_processor->get_version() == 0) {
884  // Version 00 has an extra requirement to read some bytes after the
885  // handshake
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)
890  );
891  bytes_processed += 8;
892  } else {
893  // TODO: need more bytes
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));
897  return;
898  }
899  }
900 
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")));
906  }
907  }
908 
909  // The remaining bytes in m_buf are frame data. Copy them to the
910  // beginning of the buffer and note the length. They will be read after
911  // the handshake completes and before more bytes are read.
912  std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
913  m_buf_cursor = bytes_transferred-bytes_processed;
914 
915 
916  m_internal_state = istate::PROCESS_HTTP_REQUEST;
917 
918  // We have the complete request. Process it.
919  lib::error_code handshake_ec = this->process_handshake_request();
920 
921  // Write a response if this is a websocket connection or if it is an
922  // HTTP connection for which the response has not been deferred or
923  // started yet by a different system (i.e. still in init state).
924  if (!m_is_http || m_http_state == session::http_state::init) {
925  this->write_http_response(handshake_ec);
926  }
927  } else {
928  // read at least 1 more byte
929  transport_con_type::async_read_at_least(
930  1,
931  m_buf,
933  lib::bind(
934  &type::handle_read_handshake,
935  type::get_shared(),
936  lib::placeholders::_1,
937  lib::placeholders::_2
938  )
939  );
940  }
941 }
942 
943 // write_http_response requires the request to be fully read and the connection
944 // to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors
945 // before the request is fully read (specifically at a point where we aren't
946 // sure if the hybi00 key3 bytes need to be read). This method sets the correct
947 // state and calls write_http_response
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");
954  return;
955  }
956 
957  m_internal_state = istate::PROCESS_HTTP_REQUEST;
958 
959  this->write_http_response(ec);
960 }
961 
962 // All exit paths for this function need to call write_http_response() or submit
963 // a new read request with this function as the handler.
964 template <typename config>
965 void connection<config>::handle_read_frame(lib::error_code const & ec,
966  size_t bytes_transferred)
967 {
968  //m_alog.write(log::alevel::devel,"connection handle_read_frame");
969 
970  lib::error_code ecm = ec;
971 
972  if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
974  }
975 
976  if (ecm) {
977  log::level echannel = log::elevel::rerror;
978 
979  if (ecm == transport::error::eof) {
980  if (m_state == session::state::closed) {
981  // we expect to get eof if the connection is closed already
982  // just ignore it
983  m_alog.write(log::alevel::devel,"got eof from closed con");
984  return;
985  } else if (m_state == session::state::closing && !m_is_server) {
986  // If we are a client we expect to get eof in the closing state,
987  // this is a signal to terminate our end of the connection after
988  // the closing handshake
989  terminate(lib::error_code());
990  return;
991  }
992  } else if (ecm == error::invalid_state) {
993  // In general, invalid state errors in the closed state are the
994  // result of handlers that were in the system already when the state
995  // changed and should be ignored as they pose no problems and there
996  // is nothing useful that we can do about them.
997  if (m_state == session::state::closed) {
998  m_alog.write(log::alevel::devel,
999  "handle_read_frame: got invalid istate in closed state");
1000  return;
1001  }
1002  } else if (ecm == transport::error::tls_short_read) {
1003  if (m_state == session::state::closed) {
1004  // We expect to get a TLS short read if we try to read after the
1005  // connection is closed. If this happens ignore and exit the
1006  // read frame path.
1007  terminate(lib::error_code());
1008  return;
1009  }
1010  echannel = log::elevel::rerror;
1011  } else if (ecm == transport::error::action_after_shutdown) {
1012  echannel = log::elevel::info;
1013  }
1014 
1015  log_err(echannel, "handle_read_frame", ecm);
1016  this->terminate(ecm);
1017  return;
1018  }
1019 
1020  // Boundaries checking. TODO: How much of this should be done?
1021  /*if (bytes_transferred > config::connection_read_buffer_size) {
1022  m_elog.write(log::elevel::fatal,"Fatal boundaries checking error");
1023  this->terminate(make_error_code(error::general));
1024  return;
1025  }*/
1026 
1027  size_t p = 0;
1028 
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());
1033  }
1034 
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());
1040  }
1041 
1042  lib::error_code consume_ec;
1043 
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());
1048  }
1049 
1050  p += m_processor->consume(
1051  reinterpret_cast<uint8_t*>(m_buf)+p,
1052  bytes_transferred-p,
1053  consume_ec
1054  );
1055 
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());
1060  }
1061  if (consume_ec) {
1062  log_err(log::elevel::rerror, "consume", consume_ec);
1063 
1065  this->terminate(consume_ec);
1066  return;
1067  } else {
1068  lib::error_code close_ec;
1069  this->close(
1070  processor::error::to_ws(consume_ec),
1071  consume_ec.message(),
1072  close_ec
1073  );
1074 
1075  if (close_ec) {
1076  log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
1077  this->terminate(close_ec);
1078  return;
1079  }
1080  }
1081  return;
1082  }
1083 
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());
1089  }
1090 
1091  message_ptr msg = m_processor->get_message();
1092 
1093  if (!msg) {
1094  m_alog.write(log::alevel::devel, "null message from m_processor");
1095  } else if (!is_control(msg->get_opcode())) {
1096  // data message, dispatch to user
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);
1101  }
1102  } else {
1103  process_control_frame(msg);
1104  }
1105  }
1106  }
1107 
1108  read_frame();
1109 }
1110 
1112 template <typename config>
1114  if (!m_read_flag) {
1115  return;
1116  }
1117 
1118  transport_con_type::async_read_at_least(
1119  // std::min wont work with undefined static const values.
1120  // TODO: is there a more elegant way to do this?
1121  // Need to determine if requesting 1 byte or the exact number of bytes
1122  // is better here. 1 byte lets us be a bit more responsive at a
1123  // potential expense of additional runs through handle_read_frame
1124  /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
1125  config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
1126  1,
1127  m_buf,
1129  m_handle_read_frame
1130  );
1131 }
1132 
1133 template <typename config>
1135  m_alog.write(log::alevel::devel,"initialize_processor");
1136 
1137  // if it isn't a websocket handshake nothing to do.
1138  if (!processor::is_websocket_handshake(m_request)) {
1139  return lib::error_code();
1140  }
1141 
1142  int version = processor::get_websocket_version(m_request);
1143 
1144  if (version < 0) {
1145  m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
1146  m_response.set_status(http::status_code::bad_request);
1148  }
1149 
1150  m_processor = get_processor(version);
1151 
1152  // if the processor is not null we are done
1153  if (m_processor) {
1154  return lib::error_code();
1155  }
1156 
1157  // We don't have a processor for this version. Return bad request
1158  // with Sec-WebSocket-Version header filled with values we do accept
1159  m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version");
1160  m_response.set_status(http::status_code::bad_request);
1161 
1162  std::stringstream ss;
1163  std::string sep;
1164  std::vector<int>::const_iterator it;
1165  for (it = versions_supported.begin(); it != versions_supported.end(); it++)
1166  {
1167  ss << sep << *it;
1168  sep = ",";
1169  }
1170 
1171  m_response.replace_header("Sec-WebSocket-Version",ss.str());
1173 }
1174 
1175 template <typename config>
1177  m_alog.write(log::alevel::devel,"process handshake request");
1178 
1179  if (!processor::is_websocket_handshake(m_request)) {
1180  // this is not a websocket handshake. Process as plain HTTP
1181  m_alog.write(log::alevel::devel,"HTTP REQUEST");
1182 
1183  // extract URI from request
1185  m_request,
1186  (transport_con_type::is_secure() ? "https" : "http")
1187  );
1188 
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);
1193  }
1194 
1195  if (m_http_handler) {
1196  m_is_http = true;
1197  m_http_handler(m_connection_hdl);
1198 
1199  if (m_state == session::state::closed) {
1201  }
1202  } else {
1205  }
1206 
1207  return lib::error_code();
1208  }
1209 
1210  lib::error_code ec = m_processor->validate_handshake(m_request);
1211 
1212  // Validate: make sure all required elements are present.
1213  if (ec){
1214  // Not a valid handshake request
1215  m_alog.write(log::alevel::devel, "Bad request " + ec.message());
1216  m_response.set_status(http::status_code::bad_request);
1217  return ec;
1218  }
1219 
1220  // Read extension parameters and set up values necessary for the end user
1221  // to complete extension negotiation.
1222  std::pair<lib::error_code,std::string> neg_results;
1223  neg_results = m_processor->negotiate_extensions(m_request);
1224 
1225  if (neg_results.first) {
1226  // There was a fatal error in extension parsing that should result in
1227  // a failed connection attempt.
1228  m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
1229  m_response.set_status(http::status_code::bad_request);
1230  return neg_results.first;
1231  } else {
1232  // extension negotiation succeeded, set response header accordingly
1233  // we don't send an empty extensions header because it breaks many
1234  // clients.
1235  if (neg_results.second.size() > 0) {
1236  m_response.replace_header("Sec-WebSocket-Extensions",
1237  neg_results.second);
1238  }
1239  }
1240 
1241  // extract URI from request
1242  m_uri = m_processor->get_uri(m_request);
1243 
1244 
1245  if (!m_uri->get_valid()) {
1246  m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
1247  m_response.set_status(http::status_code::bad_request);
1249  }
1250 
1251  // extract subprotocols
1252  lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1253  m_requested_subprotocols);
1254 
1255  if (subp_ec) {
1256  // should we do anything?
1257  }
1258 
1259  // Ask application to validate the connection
1260  if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1261  m_response.set_status(http::status_code::switching_protocols);
1262 
1263  // Write the appropriate response headers based on request and
1264  // processor version
1265  ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1266 
1267  if (ec) {
1268  std::stringstream s;
1269  s << "Processing error: " << ec << "(" << ec.message() << ")";
1270  m_alog.write(log::alevel::devel, s.str());
1271 
1272  m_response.set_status(http::status_code::internal_server_error);
1273  return ec;
1274  }
1275  } else {
1276  // User application has rejected the handshake
1277  m_alog.write(log::alevel::devel, "USER REJECT");
1278 
1279  // Use Bad Request if the user handler did not provide a more
1280  // specific http response error code.
1281  // TODO: is there a better default?
1282  if (m_response.get_status_code() == http::status_code::uninitialized) {
1283  m_response.set_status(http::status_code::bad_request);
1284  }
1285 
1287  }
1288 
1289  return lib::error_code();
1290 }
1291 
1292 template <typename config>
1293 void connection<config>::write_http_response(lib::error_code const & ec) {
1294  m_alog.write(log::alevel::devel,"connection write_http_response");
1295 
1297  m_alog.write(log::alevel::http,"An HTTP handler took over the connection.");
1298  return;
1299  }
1300 
1301  if (m_response.get_status_code() == http::status_code::uninitialized) {
1304  } else {
1305  m_ec = ec;
1306  }
1307 
1308  m_response.set_version("HTTP/1.1");
1309 
1310  // Set server header based on the user agent settings
1311  if (m_response.get_header("Server").empty()) {
1312  if (!m_user_agent.empty()) {
1313  m_response.replace_header("Server",m_user_agent);
1314  } else {
1315  m_response.remove_header("Server");
1316  }
1317  }
1318 
1319  // have the processor generate the raw bytes for the wire (if it exists)
1320  if (m_processor) {
1321  m_handshake_buffer = m_processor->get_raw(m_response);
1322  } else {
1323  // a processor wont exist for raw HTTP responses.
1324  m_handshake_buffer = m_response.raw();
1325  }
1326 
1327  if (m_alog.static_test(log::alevel::devel)) {
1328  m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
1329  if (!m_response.get_header("Sec-WebSocket-Key3").empty()) {
1330  m_alog.write(log::alevel::devel,
1331  utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
1332  }
1333  }
1334 
1335  // write raw bytes
1336  transport_con_type::async_write(
1337  m_handshake_buffer.data(),
1338  m_handshake_buffer.size(),
1339  lib::bind(
1340  &type::handle_write_http_response,
1341  type::get_shared(),
1342  lib::placeholders::_1
1343  )
1344  );
1345 }
1346 
1347 template <typename config>
1348 void connection<config>::handle_write_http_response(lib::error_code const & ec) {
1349  m_alog.write(log::alevel::devel,"handle_write_http_response");
1350 
1351  lib::error_code ecm = ec;
1352 
1353  if (!ecm) {
1354  scoped_lock_type lock(m_connection_state_lock);
1355 
1356  if (m_state == session::state::connecting) {
1357  if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1359  }
1360  } else if (m_state == session::state::closed) {
1361  // The connection was canceled while the response was being sent,
1362  // usually by the handshake timer. This is basically expected
1363  // (though hopefully rare) and there is nothing we can do so ignore.
1364  m_alog.write(log::alevel::devel,
1365  "handle_write_http_response invoked after connection was closed");
1366  return;
1367  } else {
1369  }
1370  }
1371 
1372  if (ecm) {
1373  if (ecm == transport::error::eof && m_state == session::state::closed) {
1374  // we expect to get eof if the connection is closed already
1375  m_alog.write(log::alevel::devel,
1376  "got (expected) eof/state error from closed con");
1377  return;
1378  }
1379 
1380  log_err(log::elevel::rerror,"handle_write_http_response",ecm);
1381  this->terminate(ecm);
1382  return;
1383  }
1384 
1385  if (m_handshake_timer) {
1386  m_handshake_timer->cancel();
1387  m_handshake_timer.reset();
1388  }
1389 
1390  if (m_response.get_status_code() != http::status_code::switching_protocols)
1391  {
1392  /*if (m_processor || m_ec == error::http_parse_error ||
1393  m_ec == error::invalid_version || m_ec == error::unsupported_version
1394  || m_ec == error::upgrade_required)
1395  {*/
1396  if (!m_is_http) {
1397  std::stringstream s;
1398  s << "Handshake ended with HTTP error: "
1399  << m_response.get_status_code();
1400  m_elog.write(log::elevel::rerror,s.str());
1401  } else {
1402  // if this was not a websocket connection, we have written
1403  // the expected response and the connection can be closed.
1404 
1405  this->log_http_result();
1406 
1407  if (m_ec) {
1408  m_alog.write(log::alevel::devel,
1409  "got to writing HTTP results with m_ec set: "+m_ec.message());
1410  }
1412  }
1413 
1414  this->terminate(m_ec);
1415  return;
1416  }
1417 
1418  this->log_open_result();
1419 
1420  m_internal_state = istate::PROCESS_CONNECTION;
1421  m_state = session::state::open;
1422 
1423  if (m_open_handler) {
1424  m_open_handler(m_connection_hdl);
1425  }
1426 
1427  this->handle_read_frame(lib::error_code(), m_buf_cursor);
1428 }
1429 
1430 template <typename config>
1432  m_alog.write(log::alevel::devel,"connection send_http_request");
1433 
1434  // TODO: origin header?
1435 
1436  // Have the protocol processor fill in the appropriate fields based on the
1437  // selected client version
1438  if (m_processor) {
1439  lib::error_code ec;
1440  ec = m_processor->client_handshake_request(m_request,m_uri,
1441  m_requested_subprotocols);
1442 
1443  if (ec) {
1444  log_err(log::elevel::fatal,"Internal library error: Processor",ec);
1445  return;
1446  }
1447  } else {
1448  m_elog.write(log::elevel::fatal,"Internal library error: missing processor");
1449  return;
1450  }
1451 
1452  // Unless the user has overridden the user agent, send generic WS++ UA.
1453  if (m_request.get_header("User-Agent").empty()) {
1454  if (!m_user_agent.empty()) {
1455  m_request.replace_header("User-Agent",m_user_agent);
1456  } else {
1457  m_request.remove_header("User-Agent");
1458  }
1459  }
1460 
1461  m_handshake_buffer = m_request.raw();
1462 
1463  if (m_alog.static_test(log::alevel::devel)) {
1464  m_alog.write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
1465  }
1466 
1467  if (m_open_handshake_timeout_dur > 0) {
1468  m_handshake_timer = transport_con_type::set_timer(
1469  m_open_handshake_timeout_dur,
1470  lib::bind(
1471  &type::handle_open_handshake_timeout,
1472  type::get_shared(),
1473  lib::placeholders::_1
1474  )
1475  );
1476  }
1477 
1478  transport_con_type::async_write(
1479  m_handshake_buffer.data(),
1480  m_handshake_buffer.size(),
1481  lib::bind(
1482  &type::handle_send_http_request,
1483  type::get_shared(),
1484  lib::placeholders::_1
1485  )
1486  );
1487 }
1488 
1489 template <typename config>
1490 void connection<config>::handle_send_http_request(lib::error_code const & ec) {
1491  m_alog.write(log::alevel::devel,"handle_send_http_request");
1492 
1493  lib::error_code ecm = ec;
1494 
1495  if (!ecm) {
1496  scoped_lock_type lock(m_connection_state_lock);
1497 
1498  if (m_state == session::state::connecting) {
1499  if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1501  } else {
1502  m_internal_state = istate::READ_HTTP_RESPONSE;
1503  }
1504  } else if (m_state == session::state::closed) {
1505  // The connection was canceled while the response was being sent,
1506  // usually by the handshake timer. This is basically expected
1507  // (though hopefully rare) and there is nothing we can do so ignore.
1508  m_alog.write(log::alevel::devel,
1509  "handle_send_http_request invoked after connection was closed");
1510  return;
1511  } else {
1513  }
1514  }
1515 
1516  if (ecm) {
1517  if (ecm == transport::error::eof && m_state == session::state::closed) {
1518  // we expect to get eof if the connection is closed already
1519  m_alog.write(log::alevel::devel,
1520  "got (expected) eof/state error from closed con");
1521  return;
1522  }
1523 
1524  log_err(log::elevel::rerror,"handle_send_http_request",ecm);
1525  this->terminate(ecm);
1526  return;
1527  }
1528 
1529  transport_con_type::async_read_at_least(
1530  1,
1531  m_buf,
1533  lib::bind(
1534  &type::handle_read_http_response,
1535  type::get_shared(),
1536  lib::placeholders::_1,
1537  lib::placeholders::_2
1538  )
1539  );
1540 }
1541 
1542 template <typename config>
1543 void connection<config>::handle_read_http_response(lib::error_code const & ec,
1544  size_t bytes_transferred)
1545 {
1546  m_alog.write(log::alevel::devel,"handle_read_http_response");
1547 
1548  lib::error_code ecm = ec;
1549 
1550  if (!ecm) {
1551  scoped_lock_type lock(m_connection_state_lock);
1552 
1553  if (m_state == session::state::connecting) {
1554  if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1556  }
1557  } else if (m_state == session::state::closed) {
1558  // The connection was canceled while the response was being sent,
1559  // usually by the handshake timer. This is basically expected
1560  // (though hopefully rare) and there is nothing we can do so ignore.
1561  m_alog.write(log::alevel::devel,
1562  "handle_read_http_response invoked after connection was closed");
1563  return;
1564  } else {
1566  }
1567  }
1568 
1569  if (ecm) {
1570  if (ecm == transport::error::eof && m_state == session::state::closed) {
1571  // we expect to get eof if the connection is closed already
1572  m_alog.write(log::alevel::devel,
1573  "got (expected) eof/state error from closed con");
1574  return;
1575  }
1576 
1577  log_err(log::elevel::rerror,"handle_read_http_response",ecm);
1578  this->terminate(ecm);
1579  return;
1580  }
1581 
1582  size_t bytes_processed = 0;
1583  // TODO: refactor this to use error codes rather than exceptions
1584  try {
1585  bytes_processed = m_response.consume(m_buf,bytes_transferred);
1586  } catch (http::exception & e) {
1587  m_elog.write(log::elevel::rerror,
1588  std::string("error in handle_read_http_response: ")+e.what());
1589  this->terminate(make_error_code(error::general));
1590  return;
1591  }
1592 
1593  m_alog.write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
1594 
1595  if (m_response.headers_ready()) {
1596  if (m_handshake_timer) {
1597  m_handshake_timer->cancel();
1598  m_handshake_timer.reset();
1599  }
1600 
1601  lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1602  m_request,
1603  m_response
1604  );
1605  if (validate_ec) {
1606  log_err(log::elevel::rerror,"Server handshake response",validate_ec);
1607  this->terminate(validate_ec);
1608  return;
1609  }
1610 
1611  // Read extension parameters and set up values necessary for the end
1612  // user to complete extension negotiation.
1613  std::pair<lib::error_code,std::string> neg_results;
1614  neg_results = m_processor->negotiate_extensions(m_response);
1615 
1616  if (neg_results.first) {
1617  // There was a fatal error in extension negotiation. For the moment
1618  // kill all connections that fail extension negotiation.
1619 
1620  // TODO: deal with cases where the response is well formed but
1621  // doesn't match the options requested by the client. Its possible
1622  // that the best behavior in this cases is to log and continue with
1623  // an unextended connection.
1624  m_alog.write(log::alevel::devel, "Extension negotiation failed: "
1625  + neg_results.first.message());
1626  this->terminate(make_error_code(error::extension_neg_failed));
1627  // TODO: close connection with reason 1010 (and list extensions)
1628  }
1629 
1630  // response is valid, connection can now be assumed to be open
1631  m_internal_state = istate::PROCESS_CONNECTION;
1632  m_state = session::state::open;
1633 
1634  this->log_open_result();
1635 
1636  if (m_open_handler) {
1637  m_open_handler(m_connection_hdl);
1638  }
1639 
1640  // The remaining bytes in m_buf are frame data. Copy them to the
1641  // beginning of the buffer and note the length. They will be read after
1642  // the handshake completes and before more bytes are read.
1643  std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1644  m_buf_cursor = bytes_transferred-bytes_processed;
1645 
1646  this->handle_read_frame(lib::error_code(), m_buf_cursor);
1647  } else {
1648  transport_con_type::async_read_at_least(
1649  1,
1650  m_buf,
1652  lib::bind(
1653  &type::handle_read_http_response,
1654  type::get_shared(),
1655  lib::placeholders::_1,
1656  lib::placeholders::_2
1657  )
1658  );
1659  }
1660 }
1661 
1662 template <typename config>
1664  lib::error_code const & ec)
1665 {
1667  m_alog.write(log::alevel::devel,"open handshake timer cancelled");
1668  } else if (ec) {
1669  m_alog.write(log::alevel::devel,
1670  "open handle_open_handshake_timeout error: "+ec.message());
1671  // TODO: ignore or fail here?
1672  } else {
1673  m_alog.write(log::alevel::devel,"open handshake timer expired");
1675  }
1676 }
1677 
1678 template <typename config>
1680  lib::error_code const & ec)
1681 {
1683  m_alog.write(log::alevel::devel,"asio close handshake timer cancelled");
1684  } else if (ec) {
1685  m_alog.write(log::alevel::devel,
1686  "asio open handle_close_handshake_timeout error: "+ec.message());
1687  // TODO: ignore or fail here?
1688  } else {
1689  m_alog.write(log::alevel::devel, "asio close handshake timer expired");
1691  }
1692 }
1693 
1694 template <typename config>
1695 void connection<config>::terminate(lib::error_code const & ec) {
1696  if (m_alog.static_test(log::alevel::devel)) {
1697  m_alog.write(log::alevel::devel,"connection terminate");
1698  }
1699 
1700  // Cancel close handshake timer
1701  if (m_handshake_timer) {
1702  m_handshake_timer->cancel();
1703  m_handshake_timer.reset();
1704  }
1705 
1706  terminate_status tstat = unknown;
1707  if (ec) {
1708  m_ec = ec;
1709  m_local_close_code = close::status::abnormal_close;
1710  m_local_close_reason = ec.message();
1711  }
1712 
1713  // TODO: does any of this need a mutex?
1714  if (m_is_http) {
1715  m_http_state = session::http_state::closed;
1716  }
1717  if (m_state == session::state::connecting) {
1718  m_state = session::state::closed;
1719  tstat = failed;
1720 
1721  // Log fail result here before socket is shut down and we can't get
1722  // the remote address, etc anymore
1723  if (m_ec != error::http_connection_ended) {
1724  log_fail_result();
1725  }
1726  } else if (m_state != session::state::closed) {
1727  m_state = session::state::closed;
1728  tstat = closed;
1729  } else {
1730  m_alog.write(log::alevel::devel,
1731  "terminate called on connection that was already terminated");
1732  return;
1733  }
1734 
1735  // TODO: choose between shutdown and close based on error code sent
1736 
1737  transport_con_type::async_shutdown(
1738  lib::bind(
1739  &type::handle_terminate,
1740  type::get_shared(),
1741  tstat,
1742  lib::placeholders::_1
1743  )
1744  );
1745 }
1746 
1747 template <typename config>
1748 void connection<config>::handle_terminate(terminate_status tstat,
1749  lib::error_code const & ec)
1750 {
1751  if (m_alog.static_test(log::alevel::devel)) {
1752  m_alog.write(log::alevel::devel,"connection handle_terminate");
1753  }
1754 
1755  if (ec) {
1756  // there was an error actually shutting down the connection
1757  log_err(log::elevel::devel,"handle_terminate",ec);
1758  }
1759 
1760  // clean shutdown
1761  if (tstat == failed) {
1762  if (m_ec != error::http_connection_ended) {
1763  if (m_fail_handler) {
1764  m_fail_handler(m_connection_hdl);
1765  }
1766  }
1767  } else if (tstat == closed) {
1768  if (m_close_handler) {
1769  m_close_handler(m_connection_hdl);
1770  }
1771  log_close_result();
1772  } else {
1773  m_elog.write(log::elevel::rerror,"Unknown terminate_status");
1774  }
1775 
1776  // call the termination handler if it exists
1777  // if it exists it might (but shouldn't) refer to a bad memory location.
1778  // If it does, we don't care and should catch and ignore it.
1779  if (m_termination_handler) {
1780  try {
1781  m_termination_handler(type::get_shared());
1782  } catch (std::exception const & e) {
1783  m_elog.write(log::elevel::warn,
1784  std::string("termination_handler call failed. Reason was: ")+e.what());
1785  }
1786  }
1787 }
1788 
1789 template <typename config>
1791  //m_alog.write(log::alevel::devel,"connection write_frame");
1792 
1793  {
1794  scoped_lock_type lock(m_write_lock);
1795 
1796  // Check the write flag. If true, there is an outstanding transport
1797  // write already. In this case we just return. The write handler will
1798  // start a new write if the write queue isn't empty. If false, we set
1799  // the write flag and proceed to initiate a transport write.
1800  if (m_write_flag) {
1801  return;
1802  }
1803 
1804  // pull off all the messages that are ready to write.
1805  // stop if we get a message marked terminal
1806  message_ptr next_message = write_pop();
1807  while (next_message) {
1808  m_current_msgs.push_back(next_message);
1809  if (!next_message->get_terminal()) {
1810  next_message = write_pop();
1811  } else {
1812  next_message = message_ptr();
1813  }
1814  }
1815 
1816  if (m_current_msgs.empty()) {
1817  // there was nothing to send
1818  return;
1819  } else {
1820  // At this point we own the next messages to be sent and are
1821  // responsible for holding the write flag until they are
1822  // successfully sent or there is some error
1823  m_write_flag = true;
1824  }
1825  }
1826 
1828  for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1829  std::string const & header = (*it)->get_header();
1830  std::string const & payload = (*it)->get_payload();
1831 
1832  m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
1833  m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
1834  }
1835 
1836  // Print detailed send stats if those log levels are enabled
1837  if (m_alog.static_test(log::alevel::frame_header)) {
1838  if (m_alog.dynamic_test(log::alevel::frame_header)) {
1839  std::stringstream general,header,payload;
1840 
1841  general << "Dispatching write containing " << m_current_msgs.size()
1842  <<" message(s) containing ";
1843  header << "Header Bytes: \n";
1844  payload << "Payload Bytes: \n";
1845 
1846  size_t hbytes = 0;
1847  size_t pbytes = 0;
1848 
1849  for (size_t i = 0; i < m_current_msgs.size(); i++) {
1850  hbytes += m_current_msgs[i]->get_header().size();
1851  pbytes += m_current_msgs[i]->get_payload().size();
1852 
1853 
1854  header << "[" << i << "] ("
1855  << m_current_msgs[i]->get_header().size() << ") "
1856  << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
1857 
1858  if (m_alog.static_test(log::alevel::frame_payload)) {
1859  if (m_alog.dynamic_test(log::alevel::frame_payload)) {
1860  payload << "[" << i << "] ("
1861  << m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
1862  << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1863  m_current_msgs[i]->get_payload() :
1864  utility::to_hex(m_current_msgs[i]->get_payload())
1865  )
1866  << "\n";
1867  }
1868  }
1869  }
1870 
1871  general << hbytes << " header bytes and " << pbytes << " payload bytes";
1872 
1873  m_alog.write(log::alevel::frame_header,general.str());
1874  m_alog.write(log::alevel::frame_header,header.str());
1875  m_alog.write(log::alevel::frame_payload,payload.str());
1876  }
1877  }
1878 
1879  transport_con_type::async_write(
1880  m_send_buffer,
1881  m_write_frame_handler
1882  );
1883 }
1884 
1885 template <typename config>
1886 void connection<config>::handle_write_frame(lib::error_code const & ec)
1887 {
1888  if (m_alog.static_test(log::alevel::devel)) {
1889  m_alog.write(log::alevel::devel,"connection handle_write_frame");
1890  }
1891 
1892  bool terminal = m_current_msgs.back()->get_terminal();
1893 
1894  m_send_buffer.clear();
1895  m_current_msgs.clear();
1896  // TODO: recycle instead of deleting
1897 
1898  if (ec) {
1899  log_err(log::elevel::fatal,"handle_write_frame",ec);
1900  this->terminate(ec);
1901  return;
1902  }
1903 
1904  if (terminal) {
1905  this->terminate(lib::error_code());
1906  return;
1907  }
1908 
1909  bool needs_writing = false;
1910  {
1911  scoped_lock_type lock(m_write_lock);
1912 
1913  // release write flag
1914  m_write_flag = false;
1915 
1916  needs_writing = !m_send_queue.empty();
1917  }
1918 
1919  if (needs_writing) {
1920  transport_con_type::dispatch(lib::bind(
1921  &type::write_frame,
1922  type::get_shared()
1923  ));
1924  }
1925 }
1926 
1927 template <typename config>
1928 std::vector<int> const & connection<config>::get_supported_versions() const
1929 {
1930  return versions_supported;
1931 }
1932 
1933 template <typename config>
1935 {
1936  m_alog.write(log::alevel::devel,"process_control_frame");
1937 
1938  frame::opcode::value op = msg->get_opcode();
1939  lib::error_code ec;
1940 
1941  std::stringstream s;
1942  s << "Control frame received with opcode " << op;
1943  m_alog.write(log::alevel::control,s.str());
1944 
1945  if (m_state == session::state::closed) {
1946  m_elog.write(log::elevel::warn,"got frame in state closed");
1947  return;
1948  }
1949  if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1950  m_elog.write(log::elevel::warn,"got non-close frame in state closing");
1951  return;
1952  }
1953 
1954  if (op == frame::opcode::PING) {
1955  bool should_reply = true;
1956 
1957  if (m_ping_handler) {
1958  should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1959  }
1960 
1961  if (should_reply) {
1962  this->pong(msg->get_payload(),ec);
1963  if (ec) {
1964  log_err(log::elevel::devel,"Failed to send response pong",ec);
1965  }
1966  }
1967  } else if (op == frame::opcode::PONG) {
1968  if (m_pong_handler) {
1969  m_pong_handler(m_connection_hdl, msg->get_payload());
1970  }
1971  if (m_ping_timer) {
1972  m_ping_timer->cancel();
1973  }
1974  } else if (op == frame::opcode::CLOSE) {
1975  m_alog.write(log::alevel::devel,"got close frame");
1976  // record close code and reason somewhere
1977 
1978  m_remote_close_code = close::extract_code(msg->get_payload(),ec);
1979  if (ec) {
1980  s.str("");
1982  s << "Received invalid close code " << m_remote_close_code
1983  << " dropping connection per config.";
1984  m_elog.write(log::elevel::devel,s.str());
1985  this->terminate(ec);
1986  } else {
1987  s << "Received invalid close code " << m_remote_close_code
1988  << " sending acknowledgement and closing";
1989  m_elog.write(log::elevel::devel,s.str());
1990  ec = send_close_ack(close::status::protocol_error,
1991  "Invalid close code");
1992  if (ec) {
1993  log_err(log::elevel::devel,"send_close_ack",ec);
1994  }
1995  }
1996  return;
1997  }
1998 
1999  m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
2000  if (ec) {
2002  m_elog.write(log::elevel::devel,
2003  "Received invalid close reason. Dropping connection per config");
2004  this->terminate(ec);
2005  } else {
2006  m_elog.write(log::elevel::devel,
2007  "Received invalid close reason. Sending acknowledgement and closing");
2008  ec = send_close_ack(close::status::protocol_error,
2009  "Invalid close reason");
2010  if (ec) {
2011  log_err(log::elevel::devel,"send_close_ack",ec);
2012  }
2013  }
2014  return;
2015  }
2016 
2017  if (m_state == session::state::open) {
2018  s.str("");
2019  s << "Received close frame with code " << m_remote_close_code
2020  << " and reason " << m_remote_close_reason;
2021  m_alog.write(log::alevel::devel,s.str());
2022 
2023  ec = send_close_ack();
2024  if (ec) {
2025  log_err(log::elevel::devel,"send_close_ack",ec);
2026  }
2027  } else if (m_state == session::state::closing && !m_was_clean) {
2028  // ack of our close
2029  m_alog.write(log::alevel::devel, "Got acknowledgement of close");
2030 
2031  m_was_clean = true;
2032 
2033  // If we are a server terminate the connection now. Clients should
2034  // leave the connection open to give the server an opportunity to
2035  // initiate the TCP close. The client's timer will handle closing
2036  // its side of the connection if the server misbehaves.
2037  //
2038  // TODO: different behavior if the underlying transport doesn't
2039  // support timers?
2040  if (m_is_server) {
2041  terminate(lib::error_code());
2042  }
2043  } else {
2044  // spurious, ignore
2045  m_elog.write(log::elevel::devel, "Got close frame in wrong state");
2046  }
2047  } else {
2048  // got an invalid control opcode
2049  m_elog.write(log::elevel::devel, "Got control frame with invalid opcode");
2050  // initiate protocol error shutdown
2051  }
2052 }
2053 
2054 template <typename config>
2056  std::string const & reason)
2057 {
2058  return send_close_frame(code,reason,true,m_is_server);
2059 }
2060 
2061 template <typename config>
2063  std::string const & reason, bool ack, bool terminal)
2064 {
2065  m_alog.write(log::alevel::devel,"send_close_frame");
2066 
2067  // check for special codes
2068 
2069  // If silent close is set, respect it and blank out close information
2070  // Otherwise use whatever has been specified in the parameters. If
2071  // parameters specifies close::status::blank then determine what to do
2072  // based on whether or not this is an ack. If it is not an ack just
2073  // send blank info. If it is an ack then echo the close information from
2074  // the remote endpoint.
2075  if (config::silent_close) {
2076  m_alog.write(log::alevel::devel,"closing silently");
2077  m_local_close_code = close::status::no_status;
2078  m_local_close_reason.clear();
2079  } else if (code != close::status::blank) {
2080  m_alog.write(log::alevel::devel,"closing with specified codes");
2081  m_local_close_code = code;
2082  m_local_close_reason = reason;
2083  } else if (!ack) {
2084  m_alog.write(log::alevel::devel,"closing with no status code");
2085  m_local_close_code = close::status::no_status;
2086  m_local_close_reason.clear();
2087  } else if (m_remote_close_code == close::status::no_status) {
2088  m_alog.write(log::alevel::devel,
2089  "acknowledging a no-status close with normal code");
2090  m_local_close_code = close::status::normal;
2091  m_local_close_reason.clear();
2092  } else {
2093  m_alog.write(log::alevel::devel,"acknowledging with remote codes");
2094  m_local_close_code = m_remote_close_code;
2095  m_local_close_reason = m_remote_close_reason;
2096  }
2097 
2098  std::stringstream s;
2099  s << "Closing with code: " << m_local_close_code << ", and reason: "
2100  << m_local_close_reason;
2101  m_alog.write(log::alevel::devel,s.str());
2102 
2103  message_ptr msg = m_msg_manager->get_message();
2104  if (!msg) {
2106  }
2107 
2108  lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2109  m_local_close_reason,msg);
2110  if (ec) {
2111  return ec;
2112  }
2113 
2114  // Messages flagged terminal will result in the TCP connection being dropped
2115  // after the message has been written. This is typically used when servers
2116  // send an ack and when any endpoint encounters a protocol error
2117  if (terminal) {
2118  msg->set_terminal(true);
2119  }
2120 
2121  m_state = session::state::closing;
2122 
2123  if (ack) {
2124  m_was_clean = true;
2125  }
2126 
2127  // Start a timer so we don't wait forever for the acknowledgement close
2128  // frame
2129  if (m_close_handshake_timeout_dur > 0) {
2130  m_handshake_timer = transport_con_type::set_timer(
2131  m_close_handshake_timeout_dur,
2132  lib::bind(
2133  &type::handle_close_handshake_timeout,
2134  type::get_shared(),
2135  lib::placeholders::_1
2136  )
2137  );
2138  }
2139 
2140  bool needs_writing = false;
2141  {
2142  scoped_lock_type lock(m_write_lock);
2143  write_push(msg);
2144  needs_writing = !m_write_flag && !m_send_queue.empty();
2145  }
2146 
2147  if (needs_writing) {
2148  transport_con_type::dispatch(lib::bind(
2149  &type::write_frame,
2150  type::get_shared()
2151  ));
2152  }
2153 
2154  return lib::error_code();
2155 }
2156 
2157 template <typename config>
2160  // TODO: allow disabling certain versions
2161 
2162  processor_ptr p;
2163 
2164  switch (version) {
2165  case 0:
2166  p = lib::make_shared<processor::hybi00<config> >(
2167  transport_con_type::is_secure(),
2168  m_is_server,
2169  m_msg_manager
2170  );
2171  break;
2172  case 7:
2173  p = lib::make_shared<processor::hybi07<config> >(
2174  transport_con_type::is_secure(),
2175  m_is_server,
2176  m_msg_manager,
2177  lib::ref(m_rng)
2178  );
2179  break;
2180  case 8:
2181  p = lib::make_shared<processor::hybi08<config> >(
2182  transport_con_type::is_secure(),
2183  m_is_server,
2184  m_msg_manager,
2185  lib::ref(m_rng)
2186  );
2187  break;
2188  case 13:
2189  p = lib::make_shared<processor::hybi13<config> >(
2190  transport_con_type::is_secure(),
2191  m_is_server,
2192  m_msg_manager,
2193  lib::ref(m_rng)
2194  );
2195  break;
2196  default:
2197  return p;
2198  }
2199 
2200  // Settings not configured by the constructor
2201  p->set_max_message_size(m_max_message_size);
2202 
2203  return p;
2204 }
2205 
2206 template <typename config>
2208 {
2209  if (!msg) {
2210  return;
2211  }
2212 
2213  m_send_buffer_size += msg->get_payload().size();
2214  m_send_queue.push(msg);
2215 
2216  if (m_alog.static_test(log::alevel::devel)) {
2217  std::stringstream s;
2218  s << "write_push: message count: " << m_send_queue.size()
2219  << " buffer size: " << m_send_buffer_size;
2220  m_alog.write(log::alevel::devel,s.str());
2221  }
2222 }
2223 
2224 template <typename config>
2226 {
2227  message_ptr msg;
2228 
2229  if (m_send_queue.empty()) {
2230  return msg;
2231  }
2232 
2233  msg = m_send_queue.front();
2234 
2235  m_send_buffer_size -= msg->get_payload().size();
2236  m_send_queue.pop();
2237 
2238  if (m_alog.static_test(log::alevel::devel)) {
2239  std::stringstream s;
2240  s << "write_pop: message count: " << m_send_queue.size()
2241  << " buffer size: " << m_send_buffer_size;
2242  m_alog.write(log::alevel::devel,s.str());
2243  }
2244  return msg;
2245 }
2246 
2247 template <typename config>
2249 {
2250  std::stringstream s;
2251 
2252  int version;
2253  if (!processor::is_websocket_handshake(m_request)) {
2254  version = -1;
2255  } else {
2256  version = processor::get_websocket_version(m_request);
2257  }
2258 
2259  // Connection Type
2260  s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
2261 
2262  // Remote endpoint address
2263  s << transport_con_type::get_remote_endpoint() << " ";
2264 
2265  // Version string if WebSocket
2266  if (version != -1) {
2267  s << "v" << version << " ";
2268  }
2269 
2270  // User Agent
2271  std::string ua = m_request.get_header("User-Agent");
2272  if (ua.empty()) {
2273  s << "\"\" ";
2274  } else {
2275  // check if there are any quotes in the user agent
2276  s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2277  }
2278 
2279  // URI
2280  s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
2281 
2282  // Status code
2283  s << m_response.get_status_code();
2284 
2285  m_alog.write(log::alevel::connect,s.str());
2286 }
2287 
2288 template <typename config>
2290 {
2291  std::stringstream s;
2292 
2293  s << "Disconnect "
2294  << "close local:[" << m_local_close_code
2295  << (m_local_close_reason.empty() ? "" : ","+m_local_close_reason)
2296  << "] remote:[" << m_remote_close_code
2297  << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]";
2298 
2299  m_alog.write(log::alevel::disconnect,s.str());
2300 }
2301 
2302 template <typename config>
2304 {
2305  std::stringstream s;
2306 
2307  int version = processor::get_websocket_version(m_request);
2308 
2309  // Connection Type
2310  s << "WebSocket Connection ";
2311 
2312  // Remote endpoint address & WebSocket version
2313  s << transport_con_type::get_remote_endpoint();
2314  if (version < 0) {
2315  s << " -";
2316  } else {
2317  s << " v" << version;
2318  }
2319 
2320  // User Agent
2321  std::string ua = m_request.get_header("User-Agent");
2322  if (ua.empty()) {
2323  s << " \"\" ";
2324  } else {
2325  // check if there are any quotes in the user agent
2326  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2327  }
2328 
2329  // URI
2330  s << (m_uri ? m_uri->get_resource() : "-");
2331 
2332  // HTTP Status code
2333  s << " " << m_response.get_status_code();
2334 
2335  // WebSocket++ error code & reason
2336  s << " " << m_ec << " " << m_ec.message();
2337 
2338  m_alog.write(log::alevel::fail,s.str());
2339 }
2340 
2341 template <typename config>
2343  std::stringstream s;
2344 
2345  if (processor::is_websocket_handshake(m_request)) {
2346  m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket");
2347  return;
2348  }
2349 
2350  // Connection Type
2351  s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host"))
2352  << " " << transport_con_type::get_remote_endpoint()
2353  << " \"" << m_request.get_method()
2354  << " " << (m_uri ? m_uri->get_resource() : "-")
2355  << " " << m_request.get_version() << "\" " << m_response.get_status_code()
2356  << " " << m_response.get_body().size();
2357 
2358  // User Agent
2359  std::string ua = m_request.get_header("User-Agent");
2360  if (ua.empty()) {
2361  s << " \"\" ";
2362  } else {
2363  // check if there are any quotes in the user agent
2364  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2365  }
2366 
2367  m_alog.write(log::alevel::http,s.str());
2368 }
2369 
2370 } // namespace websocketpp
2371 
2372 #endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
bool is_control(value v)
Check if an opcode is for a control frame.
Definition: frame.hpp:139
lib::error_code process_handshake_request()
Perform WebSocket handshake validation of m_request using m_processor.
void add_subprotocol(std::string const &request, lib::error_code &ec)
Adds the given subprotocol string to the request list (exception free)
static level const fatal
Unrecoverable error.
Definition: levels.hpp:78
websocketpp::config::asio_tls_client::message_type::ptr message_ptr
void set_termination_handler(termination_handler new_handler)
Sets the handler for a terminating connection.
void read_frame()
Issue a new transport read unless reading is paused.
std::vector< std::string > const & get_requested_subprotocols() const
Gets all of the subprotocols requested by the client.
uint16_t value
The type of a close code value.
Definition: close.hpp:49
lib::error_code make_error_code(error::value e)
Definition: error.hpp:235
static level const control
One line per control frame.
Definition: levels.hpp:125
std::string const & get_subprotocol() const
Gets the negotated subprotocol.
bool terminal(value code)
Determine if the code represents an unrecoverable error.
Definition: close.hpp:212
uri_ptr get_uri_from_host(request_type &request, std::string scheme)
Extract a URI ptr from the host header of the request.
Definition: processor.hpp:136
lib::error_code pause_reading()
Pause reading of new data.
bool is_websocket_handshake(request_type &r)
Determine whether or not a generic HTTP request is a WebSocket handshake.
Definition: processor.hpp:68
Attempted to use a client specific feature on a server endpoint.
Definition: error.hpp:105
void handle_transport_init(lib::error_code const &ec)
status_code::value m_error_code
Definition: constants.hpp:303
session::state::value get_state() const
Return the connection state.
lib::function< void(ptr)> termination_handler
Definition: connection.hpp:260
static std::vector< int > const versions_supported(helper, helper+4)
Container that stores the list of protocol versions supported.
Connection rejected.
Definition: error.hpp:130
Selected subprotocol was not requested by the client.
Definition: error.hpp:102
int get_websocket_version(request_type &r)
Extract the version from a WebSocket handshake request.
Definition: processor.hpp:107
uri_ptr get_uri() const
Gets the connection URI.
void replace_header(std::string const &key, std::string const &val)
Replace a header.
void ping(std::string const &payload)
Send a ping.
Represents an individual WebSocket connection.
Definition: connection.hpp:235
size_t get_buffered_amount() const
Get the size of the outgoing write buffer (in payload bytes)
void handle_read_http_response(lib::error_code const &ec, size_t bytes_transferred)
void terminate(lib::error_code const &ec)
static level const frame_payload
One line per frame, includes the full message payload (warning: chatty)
Definition: levels.hpp:129
static value const protocol_error
A protocol error occurred.
Definition: close.hpp:83
static value const normal
Normal closure, meaning that the purpose for which the connection was established has been fulfilled...
Definition: close.hpp:76
static level const devel
Low level debugging information (warning: very chatty)
Definition: levels.hpp:63
std::string string_replace_all(std::string subject, std::string const &search, std::string const &replace)
Replace all occurrances of a substring with another.
status::value extract_code(std::string const &payload, lib::error_code &ec)
Extract a close code value from a close payload.
Definition: close.hpp:283
void select_subprotocol(std::string const &value, lib::error_code &ec)
Select a subprotocol to use (exception free)
A fake lock guard implementation that does nothing.
Definition: none.hpp:46
std::string to_hex(std::string const &input)
Convert std::string to ascii printed string of hex digits.
void pong(std::string const &payload)
Send a pong.
void send_http_response()
Send deferred HTTP Response.
bool is_not_token_char(unsigned char c)
Is the character a non-token.
Definition: constants.hpp:103
void handle_read_handshake(lib::error_code const &ec, size_t bytes_transferred)
void handle_resume_reading()
Resume reading callback.
static level const frame_header
One line per frame, includes the full frame header.
Definition: levels.hpp:127
void handle_open_handshake_timeout(lib::error_code const &ec)
void handle_close_handshake_timeout(lib::error_code const &ec)
lib::error_code initialize_processor()
Set m_processor based on information in m_request.
static level const devel
Development messages (warning: very chatty)
Definition: levels.hpp:141
std::string const & get_request_header(std::string const &key) const
Retrieve a request header.
static const int client_version
WebSocket Protocol version to use as a client.
Table::const_iterator iterator
Definition: cs-internal.hpp:41
message_type::ptr message_ptr
Definition: connection.hpp:269
static level const disconnect
One line for each closed connection. Includes closing codes and reasons.
Definition: levels.hpp:123
lib::shared_ptr< message > ptr
Definition: message.hpp:86
lib::error_code resume_reading()
Resume reading of new data.
void start()
Start the connection state machine.
void close(close::status::value const code, std::string const &reason)
Close the connection.
lib::error_code defer_http_response()
Defer HTTP Response until later (Exception free)
Invalid WebSocket protocol version.
Definition: error.hpp:137
void set_status(http::status_code::value code)
Set response status code and message.
void handle_pong_timeout(std::string payload, lib::error_code const &ec)
Utility method that gets called back when the ping timer expires.
close::status::value to_ws(lib::error_code ec)
Converts a processor error_code into a websocket close code.
Definition: base.hpp:261
lib::error_code make_error_code(error::processor_errors e)
Create an error code with the given value and the processor category.
Definition: base.hpp:244
void append_header(std::string const &key, std::string const &val)
Append a header.
lib::error_code send(std::string const &payload, frame::opcode::value op=frame::opcode::text)
Create a message and then add it to the outgoing send queue.
The connection was in the wrong state for this operation.
Definition: error.hpp:74
static level const info
Information about minor configuration problems or additional information about other warnings...
Definition: levels.hpp:69
void write_frame()
Checks if there are frames in the send queue and if there are sends one.
void handle_write_frame(lib::error_code const &ec)
Process the results of a frame write operation and start the next write.
static const bool drop_on_protocol_error
Drop connections immediately on protocol error.
Namespace for the WebSocket++ project.
Definition: base64.hpp:41
void set_max_message_size(size_t new_value)
Set maximum message size.
Definition: connection.hpp:575
Extension negotiation failed.
Definition: error.hpp:146
void close(T *e, websocketpp::connection_hdl hdl)
std::string const & get_origin() const
Return the same origin policy origin value from the opening request.
std::string const & get_response_header(std::string const &key) const
Retrieve a response header.
uint16_t get_port() const
Returns the port component of the connection URI.
lib::shared_ptr< processor_type > processor_ptr
Definition: connection.hpp:278
std::string const & get_resource() const
Returns the resource component of the connection URI.
A simple utility buffer class.
Definition: connection.hpp:138
void handle_read_frame(lib::error_code const &ec, size_t bytes_transferred)
std::vector< int > const & get_supported_versions() const
Get array of WebSocket protocol versions that this connection supports.
The endpoint is out of outgoing message buffers.
Definition: error.hpp:68
static const size_t connection_read_buffer_size
lib::shared_ptr< uri > uri_ptr
Pointer to a URI.
Definition: uri.hpp:351
void handle_interrupt()
Transport inturrupt callback.
static value const no_status
A dummy value to indicate that no status code was received.
Definition: close.hpp:97
void handle_pause_reading()
Pause reading callback.
WebSocket close handshake timed out.
Definition: error.hpp:117
Catch-all library error.
Definition: error.hpp:47
bool get_secure() const
Returns the secure flag from the connection URI.
void handle_terminate(terminate_status tstat, lib::error_code const &ec)
static level const fail
One line for each failed WebSocket connection with details.
Definition: levels.hpp:147
static const bool silent_close
Suppresses the return of detailed connection close information.
static value const abnormal_close
A dummy value to indicate that the connection was closed abnormally.
Definition: close.hpp:104
std::string const & get_request_body() const
Retrieve a request body.
static uint8_t const close_reason_size
Maximum size of close frame reason.
Definition: frame.hpp:169
void set_uri(uri_ptr uri)
Sets the connection URI.
WebSocket opening handshake timed out.
Definition: error.hpp:114
std::string const & get_host() const
Returns the host component of the connection URI.
uint32_t level
Type of a channel package.
Definition: levels.hpp:37
void read_handshake(size_t num_bytes)
Attempted to use a server specific feature on a client endpoint.
Definition: error.hpp:108
lib::error_code interrupt()
Asyncronously invoke handler::on_inturrupt.
std::string extract_reason(std::string const &payload, lib::error_code &ec)
Extract the reason string from a close payload.
Definition: close.hpp:322
virtual const char * what() const
Definition: constants.hpp:296
static level const rerror
Recoverable error.
Definition: levels.hpp:75
An invalid uri was supplied.
Definition: error.hpp:65
void handle_send_http_request(lib::error_code const &ec)
static level const warn
Information about important problems not severe enough to terminate connections.
Definition: levels.hpp:72
void handle_write_http_response(lib::error_code const &ec)
Unsupported WebSocket protocol version.
Definition: error.hpp:140
static value const blank
A blank value for internal use.
Definition: close.hpp:52
static level const connect
Information about new connections.
Definition: levels.hpp:121
static level const http
Access related to HTTP requests.
Definition: levels.hpp:145
void remove_header(std::string const &key)
Remove a header.
void set_body(std::string const &value)
Set response body content.