NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: 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 
501  std::vector<std::string>::iterator it;
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::action_after_shutdown) {
1003  echannel = log::elevel::info;
1004  } else {
1005  // TODO: more generally should we do something different here in the
1006  // case that m_state is cosed? Are errors after the connection is
1007  // already closed really an rerror?
1008  }
1009 
1010 
1011 
1012  log_err(echannel, "handle_read_frame", ecm);
1013  this->terminate(ecm);
1014  return;
1015  }
1016 
1017  // Boundaries checking. TODO: How much of this should be done?
1018  /*if (bytes_transferred > config::connection_read_buffer_size) {
1019  m_elog->write(log::elevel::fatal,"Fatal boundaries checking error");
1020  this->terminate(make_error_code(error::general));
1021  return;
1022  }*/
1023 
1024  size_t p = 0;
1025 
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());
1030  }
1031 
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());
1037  }
1038 
1039  lib::error_code consume_ec;
1040 
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());
1045  }
1046 
1047  p += m_processor->consume(
1048  reinterpret_cast<uint8_t*>(m_buf)+p,
1049  bytes_transferred-p,
1050  consume_ec
1051  );
1052 
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());
1057  }
1058  if (consume_ec) {
1059  log_err(log::elevel::rerror, "consume", consume_ec);
1060 
1062  this->terminate(consume_ec);
1063  return;
1064  } else {
1065  lib::error_code close_ec;
1066  this->close(
1067  processor::error::to_ws(consume_ec),
1068  consume_ec.message(),
1069  close_ec
1070  );
1071 
1072  if (close_ec) {
1073  log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
1074  this->terminate(close_ec);
1075  return;
1076  }
1077  }
1078  return;
1079  }
1080 
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());
1086  }
1087 
1088  message_ptr msg = m_processor->get_message();
1089 
1090  if (!msg) {
1091  m_alog->write(log::alevel::devel, "null message from m_processor");
1092  } else if (!is_control(msg->get_opcode())) {
1093  // data message, dispatch to user
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);
1098  }
1099  } else {
1100  process_control_frame(msg);
1101  }
1102  }
1103  }
1104 
1105  read_frame();
1106 }
1107 
1109 template <typename config>
1111  if (!m_read_flag) {
1112  return;
1113  }
1114 
1115  transport_con_type::async_read_at_least(
1116  // std::min wont work with undefined static const values.
1117  // TODO: is there a more elegant way to do this?
1118  // Need to determine if requesting 1 byte or the exact number of bytes
1119  // is better here. 1 byte lets us be a bit more responsive at a
1120  // potential expense of additional runs through handle_read_frame
1121  /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
1122  config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
1123  1,
1124  m_buf,
1126  m_handle_read_frame
1127  );
1128 }
1129 
1130 template <typename config>
1132  m_alog->write(log::alevel::devel,"initialize_processor");
1133 
1134  // if it isn't a websocket handshake nothing to do.
1135  if (!processor::is_websocket_handshake(m_request)) {
1136  return lib::error_code();
1137  }
1138 
1139  int version = processor::get_websocket_version(m_request);
1140 
1141  if (version < 0) {
1142  m_alog->write(log::alevel::devel, "BAD REQUEST: can't determine version");
1143  m_response.set_status(http::status_code::bad_request);
1145  }
1146 
1147  m_processor = get_processor(version);
1148 
1149  // if the processor is not null we are done
1150  if (m_processor) {
1151  return lib::error_code();
1152  }
1153 
1154  // We don't have a processor for this version. Return bad request
1155  // with Sec-WebSocket-Version header filled with values we do accept
1156  m_alog->write(log::alevel::devel, "BAD REQUEST: no processor for version");
1157  m_response.set_status(http::status_code::bad_request);
1158 
1159  std::stringstream ss;
1160  std::string sep;
1161  std::vector<int>::const_iterator it;
1162  for (it = versions_supported.begin(); it != versions_supported.end(); it++)
1163  {
1164  ss << sep << *it;
1165  sep = ",";
1166  }
1167 
1168  m_response.replace_header("Sec-WebSocket-Version",ss.str());
1170 }
1171 
1172 template <typename config>
1174  m_alog->write(log::alevel::devel,"process handshake request");
1175 
1176  if (!processor::is_websocket_handshake(m_request)) {
1177  // this is not a websocket handshake. Process as plain HTTP
1178  m_alog->write(log::alevel::devel,"HTTP REQUEST");
1179 
1180  // extract URI from request
1182  m_request,
1183  (transport_con_type::is_secure() ? "https" : "http")
1184  );
1185 
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);
1190  }
1191 
1192  if (m_http_handler) {
1193  m_is_http = true;
1194  m_http_handler(m_connection_hdl);
1195 
1196  if (m_state == session::state::closed) {
1198  }
1199  } else {
1202  }
1203 
1204  return lib::error_code();
1205  }
1206 
1207  lib::error_code ec = m_processor->validate_handshake(m_request);
1208 
1209  // Validate: make sure all required elements are present.
1210  if (ec){
1211  // Not a valid handshake request
1212  m_alog->write(log::alevel::devel, "Bad request " + ec.message());
1213  m_response.set_status(http::status_code::bad_request);
1214  return ec;
1215  }
1216 
1217  // Read extension parameters and set up values necessary for the end user
1218  // to complete extension negotiation.
1219  std::pair<lib::error_code,std::string> neg_results;
1220  neg_results = m_processor->negotiate_extensions(m_request);
1221 
1223  // There was a fatal error in extension parsing that should result in
1224  // a failed connection attempt.
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) {
1229  // There was a fatal error in extension processing that is probably our
1230  // fault. Consider extension negotiation to have failed and continue as
1231  // if extensions were not supported
1232  m_elog->write(log::elevel::info,
1233  "Extension negotiation failed: " + neg_results.first.message());
1234  } else {
1235  // extension negotiation succeeded, set response header accordingly
1236  // we don't send an empty extensions header because it breaks many
1237  // clients.
1238  if (neg_results.second.size() > 0) {
1239  m_response.replace_header("Sec-WebSocket-Extensions",
1240  neg_results.second);
1241  }
1242  }
1243 
1244  // extract URI from request
1245  m_uri = m_processor->get_uri(m_request);
1246 
1247 
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);
1252  }
1253 
1254  // extract subprotocols
1255  lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1256  m_requested_subprotocols);
1257 
1258  if (subp_ec) {
1259  // should we do anything?
1260  }
1261 
1262  // Ask application to validate the connection
1263  if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1264  m_response.set_status(http::status_code::switching_protocols);
1265 
1266  // Write the appropriate response headers based on request and
1267  // processor version
1268  ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1269 
1270  if (ec) {
1271  std::stringstream s;
1272  s << "Processing error: " << ec << "(" << ec.message() << ")";
1273  m_alog->write(log::alevel::devel, s.str());
1274 
1275  m_response.set_status(http::status_code::internal_server_error);
1276  return ec;
1277  }
1278  } else {
1279  // User application has rejected the handshake
1280  m_alog->write(log::alevel::devel, "USER REJECT");
1281 
1282  // Use Bad Request if the user handler did not provide a more
1283  // specific http response error code.
1284  // TODO: is there a better default?
1285  if (m_response.get_status_code() == http::status_code::uninitialized) {
1286  m_response.set_status(http::status_code::bad_request);
1287  }
1288 
1290  }
1291 
1292  return lib::error_code();
1293 }
1294 
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");
1298 
1300  m_alog->write(log::alevel::http,"An HTTP handler took over the connection.");
1301  return;
1302  }
1303 
1304  if (m_response.get_status_code() == http::status_code::uninitialized) {
1307  } else {
1308  m_ec = ec;
1309  }
1310 
1311  m_response.set_version("HTTP/1.1");
1312 
1313  // Set server header based on the user agent settings
1314  if (m_response.get_header("Server").empty()) {
1315  if (!m_user_agent.empty()) {
1316  m_response.replace_header("Server",m_user_agent);
1317  } else {
1318  m_response.remove_header("Server");
1319  }
1320  }
1321 
1322  // have the processor generate the raw bytes for the wire (if it exists)
1323  if (m_processor) {
1324  m_handshake_buffer = m_processor->get_raw(m_response);
1325  } else {
1326  // a processor wont exist for raw HTTP responses.
1327  m_handshake_buffer = m_response.raw();
1328  }
1329 
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")));
1335  }
1336  }
1337 
1338  // write raw bytes
1339  transport_con_type::async_write(
1340  m_handshake_buffer.data(),
1341  m_handshake_buffer.size(),
1342  lib::bind(
1343  &type::handle_write_http_response,
1344  type::get_shared(),
1345  lib::placeholders::_1
1346  )
1347  );
1348 }
1349 
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");
1353 
1354  lib::error_code ecm = ec;
1355 
1356  if (!ecm) {
1357  scoped_lock_type lock(m_connection_state_lock);
1358 
1359  if (m_state == session::state::connecting) {
1360  if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1362  }
1363  } else if (m_state == session::state::closed) {
1364  // The connection was canceled while the response was being sent,
1365  // usually by the handshake timer. This is basically expected
1366  // (though hopefully rare) and there is nothing we can do so ignore.
1367  m_alog->write(log::alevel::devel,
1368  "handle_write_http_response invoked after connection was closed");
1369  return;
1370  } else {
1372  }
1373  }
1374 
1375  if (ecm) {
1376  if (ecm == transport::error::eof && m_state == session::state::closed) {
1377  // we expect to get eof if the connection is closed already
1378  m_alog->write(log::alevel::devel,
1379  "got (expected) eof/state error from closed con");
1380  return;
1381  }
1382 
1383  log_err(log::elevel::rerror,"handle_write_http_response",ecm);
1384  this->terminate(ecm);
1385  return;
1386  }
1387 
1388  if (m_handshake_timer) {
1389  m_handshake_timer->cancel();
1390  m_handshake_timer.reset();
1391  }
1392 
1393  if (m_response.get_status_code() != http::status_code::switching_protocols)
1394  {
1395  /*if (m_processor || m_ec == error::http_parse_error ||
1396  m_ec == error::invalid_version || m_ec == error::unsupported_version
1397  || m_ec == error::upgrade_required)
1398  {*/
1399  if (!m_is_http) {
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());
1404  } else {
1405  // if this was not a websocket connection, we have written
1406  // the expected response and the connection can be closed.
1407 
1408  this->log_http_result();
1409 
1410  if (m_ec) {
1411  m_alog->write(log::alevel::devel,
1412  "got to writing HTTP results with m_ec set: "+m_ec.message());
1413  }
1415  }
1416 
1417  this->terminate(m_ec);
1418  return;
1419  }
1420 
1421  this->log_open_result();
1422 
1423  m_internal_state = istate::PROCESS_CONNECTION;
1424  m_state = session::state::open;
1425 
1426  if (m_open_handler) {
1427  m_open_handler(m_connection_hdl);
1428  }
1429 
1430  this->handle_read_frame(lib::error_code(), m_buf_cursor);
1431 }
1432 
1433 template <typename config>
1435  m_alog->write(log::alevel::devel,"connection send_http_request");
1436 
1437  // TODO: origin header?
1438 
1439  // Have the protocol processor fill in the appropriate fields based on the
1440  // selected client version
1441  if (m_processor) {
1442  lib::error_code ec;
1443  ec = m_processor->client_handshake_request(m_request,m_uri,
1444  m_requested_subprotocols);
1445 
1446  if (ec) {
1447  log_err(log::elevel::fatal,"Internal library error: Processor",ec);
1448  return;
1449  }
1450  } else {
1451  m_elog->write(log::elevel::fatal,"Internal library error: missing processor");
1452  return;
1453  }
1454 
1455  // Unless the user has overridden the user agent, send generic WS++ UA.
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);
1459  } else {
1460  m_request.remove_header("User-Agent");
1461  }
1462  }
1463 
1464  m_handshake_buffer = m_request.raw();
1465 
1466  if (m_alog->static_test(log::alevel::devel)) {
1467  m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
1468  }
1469 
1470  if (m_open_handshake_timeout_dur > 0) {
1471  m_handshake_timer = transport_con_type::set_timer(
1472  m_open_handshake_timeout_dur,
1473  lib::bind(
1474  &type::handle_open_handshake_timeout,
1475  type::get_shared(),
1476  lib::placeholders::_1
1477  )
1478  );
1479  }
1480 
1481  transport_con_type::async_write(
1482  m_handshake_buffer.data(),
1483  m_handshake_buffer.size(),
1484  lib::bind(
1485  &type::handle_send_http_request,
1486  type::get_shared(),
1487  lib::placeholders::_1
1488  )
1489  );
1490 }
1491 
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");
1495 
1496  lib::error_code ecm = ec;
1497 
1498  if (!ecm) {
1499  scoped_lock_type lock(m_connection_state_lock);
1500 
1501  if (m_state == session::state::connecting) {
1502  if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1504  } else {
1505  m_internal_state = istate::READ_HTTP_RESPONSE;
1506  }
1507  } else if (m_state == session::state::closed) {
1508  // The connection was canceled while the response was being sent,
1509  // usually by the handshake timer. This is basically expected
1510  // (though hopefully rare) and there is nothing we can do so ignore.
1511  m_alog->write(log::alevel::devel,
1512  "handle_send_http_request invoked after connection was closed");
1513  return;
1514  } else {
1516  }
1517  }
1518 
1519  if (ecm) {
1520  if (ecm == transport::error::eof && m_state == session::state::closed) {
1521  // we expect to get eof if the connection is closed already
1522  m_alog->write(log::alevel::devel,
1523  "got (expected) eof/state error from closed con");
1524  return;
1525  }
1526 
1527  log_err(log::elevel::rerror,"handle_send_http_request",ecm);
1528  this->terminate(ecm);
1529  return;
1530  }
1531 
1532  transport_con_type::async_read_at_least(
1533  1,
1534  m_buf,
1536  lib::bind(
1537  &type::handle_read_http_response,
1538  type::get_shared(),
1539  lib::placeholders::_1,
1540  lib::placeholders::_2
1541  )
1542  );
1543 }
1544 
1545 template <typename config>
1546 void connection<config>::handle_read_http_response(lib::error_code const & ec,
1547  size_t bytes_transferred)
1548 {
1549  m_alog->write(log::alevel::devel,"handle_read_http_response");
1550 
1551  lib::error_code ecm = ec;
1552 
1553  if (!ecm) {
1554  scoped_lock_type lock(m_connection_state_lock);
1555 
1556  if (m_state == session::state::connecting) {
1557  if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1559  }
1560  } else if (m_state == session::state::closed) {
1561  // The connection was canceled while the response was being sent,
1562  // usually by the handshake timer. This is basically expected
1563  // (though hopefully rare) and there is nothing we can do so ignore.
1564  m_alog->write(log::alevel::devel,
1565  "handle_read_http_response invoked after connection was closed");
1566  return;
1567  } else {
1569  }
1570  }
1571 
1572  if (ecm) {
1573  if (ecm == transport::error::eof && m_state == session::state::closed) {
1574  // we expect to get eof if the connection is closed already
1575  m_alog->write(log::alevel::devel,
1576  "got (expected) eof/state error from closed con");
1577  return;
1578  }
1579 
1580  log_err(log::elevel::rerror,"handle_read_http_response",ecm);
1581  this->terminate(ecm);
1582  return;
1583  }
1584 
1585  size_t bytes_processed = 0;
1586  // TODO: refactor this to use error codes rather than exceptions
1587  try {
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));
1593  return;
1594  }
1595 
1596  m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
1597 
1598  if (m_response.headers_ready()) {
1599  if (m_handshake_timer) {
1600  m_handshake_timer->cancel();
1601  m_handshake_timer.reset();
1602  }
1603 
1604  lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1605  m_request,
1606  m_response
1607  );
1608  if (validate_ec) {
1609  log_err(log::elevel::rerror,"Server handshake response",validate_ec);
1610  this->terminate(validate_ec);
1611  return;
1612  }
1613 
1614  // Read extension parameters and set up values necessary for the end
1615  // user to complete extension negotiation.
1616  std::pair<lib::error_code,std::string> neg_results;
1617  neg_results = m_processor->negotiate_extensions(m_response);
1618 
1619  if (neg_results.first) {
1620  // There was a fatal error in extension negotiation. For the moment
1621  // kill all connections that fail extension negotiation.
1622 
1623  // TODO: deal with cases where the response is well formed but
1624  // doesn't match the options requested by the client. Its possible
1625  // that the best behavior in this cases is to log and continue with
1626  // an unextended connection.
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));
1630  // TODO: close connection with reason 1010 (and list extensions)
1631  }
1632 
1633  // response is valid, connection can now be assumed to be open
1634  m_internal_state = istate::PROCESS_CONNECTION;
1635  m_state = session::state::open;
1636 
1637  this->log_open_result();
1638 
1639  if (m_open_handler) {
1640  m_open_handler(m_connection_hdl);
1641  }
1642 
1643  // The remaining bytes in m_buf are frame data. Copy them to the
1644  // beginning of the buffer and note the length. They will be read after
1645  // the handshake completes and before more bytes are read.
1646  std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1647  m_buf_cursor = bytes_transferred-bytes_processed;
1648 
1649  this->handle_read_frame(lib::error_code(), m_buf_cursor);
1650  } else {
1651  transport_con_type::async_read_at_least(
1652  1,
1653  m_buf,
1655  lib::bind(
1656  &type::handle_read_http_response,
1657  type::get_shared(),
1658  lib::placeholders::_1,
1659  lib::placeholders::_2
1660  )
1661  );
1662  }
1663 }
1664 
1665 template <typename config>
1667  lib::error_code const & ec)
1668 {
1670  m_alog->write(log::alevel::devel,"open handshake timer cancelled");
1671  } else if (ec) {
1672  m_alog->write(log::alevel::devel,
1673  "open handle_open_handshake_timeout error: "+ec.message());
1674  // TODO: ignore or fail here?
1675  } else {
1676  m_alog->write(log::alevel::devel,"open handshake timer expired");
1678  }
1679 }
1680 
1681 template <typename config>
1683  lib::error_code const & ec)
1684 {
1686  m_alog->write(log::alevel::devel,"asio close handshake timer cancelled");
1687  } else if (ec) {
1688  m_alog->write(log::alevel::devel,
1689  "asio open handle_close_handshake_timeout error: "+ec.message());
1690  // TODO: ignore or fail here?
1691  } else {
1692  m_alog->write(log::alevel::devel, "asio close handshake timer expired");
1694  }
1695 }
1696 
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");
1701  }
1702 
1703  // Cancel close handshake timer
1704  if (m_handshake_timer) {
1705  m_handshake_timer->cancel();
1706  m_handshake_timer.reset();
1707  }
1708 
1709  terminate_status tstat = unknown;
1710  if (ec) {
1711  m_ec = ec;
1712  m_local_close_code = close::status::abnormal_close;
1713  m_local_close_reason = ec.message();
1714  }
1715 
1716  // TODO: does any of this need a mutex?
1717  if (m_is_http) {
1718  m_http_state = session::http_state::closed;
1719  }
1720  if (m_state == session::state::connecting) {
1721  m_state = session::state::closed;
1722  tstat = failed;
1723 
1724  // Log fail result here before socket is shut down and we can't get
1725  // the remote address, etc anymore
1726  if (m_ec != error::http_connection_ended) {
1727  log_fail_result();
1728  }
1729  } else if (m_state != session::state::closed) {
1730  m_state = session::state::closed;
1731  tstat = closed;
1732  } else {
1733  m_alog->write(log::alevel::devel,
1734  "terminate called on connection that was already terminated");
1735  return;
1736  }
1737 
1738  // TODO: choose between shutdown and close based on error code sent
1739 
1740  transport_con_type::async_shutdown(
1741  lib::bind(
1742  &type::handle_terminate,
1743  type::get_shared(),
1744  tstat,
1745  lib::placeholders::_1
1746  )
1747  );
1748 }
1749 
1750 template <typename config>
1751 void connection<config>::handle_terminate(terminate_status tstat,
1752  lib::error_code const & ec)
1753 {
1754  if (m_alog->static_test(log::alevel::devel)) {
1755  m_alog->write(log::alevel::devel,"connection handle_terminate");
1756  }
1757 
1758  if (ec) {
1759  // there was an error actually shutting down the connection
1760  log_err(log::elevel::devel,"handle_terminate",ec);
1761  }
1762 
1763  // clean shutdown
1764  if (tstat == failed) {
1765  if (m_ec != error::http_connection_ended) {
1766  if (m_fail_handler) {
1767  m_fail_handler(m_connection_hdl);
1768  }
1769  }
1770  } else if (tstat == closed) {
1771  if (m_close_handler) {
1772  m_close_handler(m_connection_hdl);
1773  }
1774  log_close_result();
1775  } else {
1776  m_elog->write(log::elevel::rerror,"Unknown terminate_status");
1777  }
1778 
1779  // call the termination handler if it exists
1780  // if it exists it might (but shouldn't) refer to a bad memory location.
1781  // If it does, we don't care and should catch and ignore it.
1782  if (m_termination_handler) {
1783  try {
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());
1788  }
1789  }
1790 }
1791 
1792 template <typename config>
1794  //m_alog->write(log::alevel::devel,"connection write_frame");
1795 
1796  {
1797  scoped_lock_type lock(m_write_lock);
1798 
1799  // Check the write flag. If true, there is an outstanding transport
1800  // write already. In this case we just return. The write handler will
1801  // start a new write if the write queue isn't empty. If false, we set
1802  // the write flag and proceed to initiate a transport write.
1803  if (m_write_flag) {
1804  return;
1805  }
1806 
1807  // pull off all the messages that are ready to write.
1808  // stop if we get a message marked terminal
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();
1814  } else {
1815  next_message = message_ptr();
1816  }
1817  }
1818 
1819  if (m_current_msgs.empty()) {
1820  // there was nothing to send
1821  return;
1822  } else {
1823  // At this point we own the next messages to be sent and are
1824  // responsible for holding the write flag until they are
1825  // successfully sent or there is some error
1826  m_write_flag = true;
1827  }
1828  }
1829 
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();
1834 
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()));
1837  }
1838 
1839  // Print detailed send stats if those log levels are enabled
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;
1843 
1844  general << "Dispatching write containing " << m_current_msgs.size()
1845  <<" message(s) containing ";
1846  header << "Header Bytes: \n";
1847  payload << "Payload Bytes: \n";
1848 
1849  size_t hbytes = 0;
1850  size_t pbytes = 0;
1851 
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();
1855 
1856 
1857  header << "[" << i << "] ("
1858  << m_current_msgs[i]->get_header().size() << ") "
1859  << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
1860 
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())
1868  )
1869  << "\n";
1870  }
1871  }
1872  }
1873 
1874  general << hbytes << " header bytes and " << pbytes << " payload bytes";
1875 
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());
1879  }
1880  }
1881 
1882  transport_con_type::async_write(
1883  m_send_buffer,
1884  m_write_frame_handler
1885  );
1886 }
1887 
1888 template <typename config>
1889 void connection<config>::handle_write_frame(lib::error_code const & ec)
1890 {
1891  if (m_alog->static_test(log::alevel::devel)) {
1892  m_alog->write(log::alevel::devel,"connection handle_write_frame");
1893  }
1894 
1895  bool terminal = m_current_msgs.back()->get_terminal();
1896 
1897  m_send_buffer.clear();
1898  m_current_msgs.clear();
1899  // TODO: recycle instead of deleting
1900 
1901  if (ec) {
1902  log_err(log::elevel::fatal,"handle_write_frame",ec);
1903  this->terminate(ec);
1904  return;
1905  }
1906 
1907  if (terminal) {
1908  this->terminate(lib::error_code());
1909  return;
1910  }
1911 
1912  bool needs_writing = false;
1913  {
1914  scoped_lock_type lock(m_write_lock);
1915 
1916  // release write flag
1917  m_write_flag = false;
1918 
1919  needs_writing = !m_send_queue.empty();
1920  }
1921 
1922  if (needs_writing) {
1923  transport_con_type::dispatch(lib::bind(
1924  &type::write_frame,
1925  type::get_shared()
1926  ));
1927  }
1928 }
1929 
1930 template <typename config>
1931 std::vector<int> const & connection<config>::get_supported_versions() const
1932 {
1933  return versions_supported;
1934 }
1935 
1936 template <typename config>
1938 {
1939  m_alog->write(log::alevel::devel,"process_control_frame");
1940 
1941  frame::opcode::value op = msg->get_opcode();
1942  lib::error_code ec;
1943 
1944  std::stringstream s;
1945  s << "Control frame received with opcode " << op;
1946  m_alog->write(log::alevel::control,s.str());
1947 
1948  if (m_state == session::state::closed) {
1949  m_elog->write(log::elevel::warn,"got frame in state closed");
1950  return;
1951  }
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");
1954  return;
1955  }
1956 
1957  if (op == frame::opcode::PING) {
1958  bool should_reply = true;
1959 
1960  if (m_ping_handler) {
1961  should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1962  }
1963 
1964  if (should_reply) {
1965  this->pong(msg->get_payload(),ec);
1966  if (ec) {
1967  log_err(log::elevel::devel,"Failed to send response pong",ec);
1968  }
1969  }
1970  } else if (op == frame::opcode::PONG) {
1971  if (m_pong_handler) {
1972  m_pong_handler(m_connection_hdl, msg->get_payload());
1973  }
1974  if (m_ping_timer) {
1975  m_ping_timer->cancel();
1976  }
1977  } else if (op == frame::opcode::CLOSE) {
1978  m_alog->write(log::alevel::devel,"got close frame");
1979  // record close code and reason somewhere
1980 
1981  m_remote_close_code = close::extract_code(msg->get_payload(),ec);
1982  if (ec) {
1983  s.str("");
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);
1989  } else {
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");
1995  if (ec) {
1996  log_err(log::elevel::devel,"send_close_ack",ec);
1997  }
1998  }
1999  return;
2000  }
2001 
2002  m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
2003  if (ec) {
2005  m_elog->write(log::elevel::devel,
2006  "Received invalid close reason. Dropping connection per config");
2007  this->terminate(ec);
2008  } else {
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");
2013  if (ec) {
2014  log_err(log::elevel::devel,"send_close_ack",ec);
2015  }
2016  }
2017  return;
2018  }
2019 
2020  if (m_state == session::state::open) {
2021  s.str("");
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());
2025 
2026  ec = send_close_ack();
2027  if (ec) {
2028  log_err(log::elevel::devel,"send_close_ack",ec);
2029  }
2030  } else if (m_state == session::state::closing && !m_was_clean) {
2031  // ack of our close
2032  m_alog->write(log::alevel::devel, "Got acknowledgement of close");
2033 
2034  m_was_clean = true;
2035 
2036  // If we are a server terminate the connection now. Clients should
2037  // leave the connection open to give the server an opportunity to
2038  // initiate the TCP close. The client's timer will handle closing
2039  // its side of the connection if the server misbehaves.
2040  //
2041  // TODO: different behavior if the underlying transport doesn't
2042  // support timers?
2043  if (m_is_server) {
2044  terminate(lib::error_code());
2045  }
2046  } else {
2047  // spurious, ignore
2048  m_elog->write(log::elevel::devel, "Got close frame in wrong state");
2049  }
2050  } else {
2051  // got an invalid control opcode
2052  m_elog->write(log::elevel::devel, "Got control frame with invalid opcode");
2053  // initiate protocol error shutdown
2054  }
2055 }
2056 
2057 template <typename config>
2059  std::string const & reason)
2060 {
2061  return send_close_frame(code,reason,true,m_is_server);
2062 }
2063 
2064 template <typename config>
2066  std::string const & reason, bool ack, bool terminal)
2067 {
2068  m_alog->write(log::alevel::devel,"send_close_frame");
2069 
2070  // check for special codes
2071 
2072  // If silent close is set, respect it and blank out close information
2073  // Otherwise use whatever has been specified in the parameters. If
2074  // parameters specifies close::status::blank then determine what to do
2075  // based on whether or not this is an ack. If it is not an ack just
2076  // send blank info. If it is an ack then echo the close information from
2077  // the remote endpoint.
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;
2086  } else if (!ack) {
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();
2095  } else {
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;
2099  }
2100 
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());
2105 
2106  message_ptr msg = m_msg_manager->get_message();
2107  if (!msg) {
2109  }
2110 
2111  lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2112  m_local_close_reason,msg);
2113  if (ec) {
2114  return ec;
2115  }
2116 
2117  // Messages flagged terminal will result in the TCP connection being dropped
2118  // after the message has been written. This is typically used when servers
2119  // send an ack and when any endpoint encounters a protocol error
2120  if (terminal) {
2121  msg->set_terminal(true);
2122  }
2123 
2124  m_state = session::state::closing;
2125 
2126  if (ack) {
2127  m_was_clean = true;
2128  }
2129 
2130  // Start a timer so we don't wait forever for the acknowledgement close
2131  // frame
2132  if (m_close_handshake_timeout_dur > 0) {
2133  m_handshake_timer = transport_con_type::set_timer(
2134  m_close_handshake_timeout_dur,
2135  lib::bind(
2136  &type::handle_close_handshake_timeout,
2137  type::get_shared(),
2138  lib::placeholders::_1
2139  )
2140  );
2141  }
2142 
2143  bool needs_writing = false;
2144  {
2145  scoped_lock_type lock(m_write_lock);
2146  write_push(msg);
2147  needs_writing = !m_write_flag && !m_send_queue.empty();
2148  }
2149 
2150  if (needs_writing) {
2151  transport_con_type::dispatch(lib::bind(
2152  &type::write_frame,
2153  type::get_shared()
2154  ));
2155  }
2156 
2157  return lib::error_code();
2158 }
2159 
2160 template <typename config>
2163  // TODO: allow disabling certain versions
2164 
2165  processor_ptr p;
2166 
2167  switch (version) {
2168  case 0:
2169  p = lib::make_shared<processor::hybi00<config> >(
2170  transport_con_type::is_secure(),
2171  m_is_server,
2172  m_msg_manager
2173  );
2174  break;
2175  case 7:
2176  p = lib::make_shared<processor::hybi07<config> >(
2177  transport_con_type::is_secure(),
2178  m_is_server,
2179  m_msg_manager,
2180  m_rng
2181  );
2182  break;
2183  case 8:
2184  p = lib::make_shared<processor::hybi08<config> >(
2185  transport_con_type::is_secure(),
2186  m_is_server,
2187  m_msg_manager,
2188  m_rng
2189  );
2190  break;
2191  case 13:
2192  p = lib::make_shared<processor::hybi13<config> >(
2193  transport_con_type::is_secure(),
2194  m_is_server,
2195  m_msg_manager,
2196  m_rng
2197  );
2198  break;
2199  default:
2200  return p;
2201  }
2202 
2203  // Settings not configured by the constructor
2204  p->set_max_message_size(m_max_message_size);
2205 
2206  return p;
2207 }
2208 
2209 template <typename config>
2211 {
2212  if (!msg) {
2213  return;
2214  }
2215 
2216  m_send_buffer_size += msg->get_payload().size();
2217  m_send_queue.push(msg);
2218 
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());
2224  }
2225 }
2226 
2227 template <typename config>
2229 {
2230  message_ptr msg;
2231 
2232  if (m_send_queue.empty()) {
2233  return msg;
2234  }
2235 
2236  msg = m_send_queue.front();
2237 
2238  m_send_buffer_size -= msg->get_payload().size();
2239  m_send_queue.pop();
2240 
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());
2246  }
2247  return msg;
2248 }
2249 
2250 template <typename config>
2252 {
2253  std::stringstream s;
2254 
2255  int version;
2256  if (!processor::is_websocket_handshake(m_request)) {
2257  version = -1;
2258  } else {
2259  version = processor::get_websocket_version(m_request);
2260  }
2261 
2262  // Connection Type
2263  s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
2264 
2265  // Remote endpoint address
2266  s << transport_con_type::get_remote_endpoint() << " ";
2267 
2268  // Version string if WebSocket
2269  if (version != -1) {
2270  s << "v" << version << " ";
2271  }
2272 
2273  // User Agent
2274  std::string ua = m_request.get_header("User-Agent");
2275  if (ua.empty()) {
2276  s << "\"\" ";
2277  } else {
2278  // check if there are any quotes in the user agent
2279  s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2280  }
2281 
2282  // URI
2283  s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
2284 
2285  // Status code
2286  s << m_response.get_status_code();
2287 
2288  m_alog->write(log::alevel::connect,s.str());
2289 }
2290 
2291 template <typename config>
2293 {
2294  std::stringstream s;
2295 
2296  s << "Disconnect "
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) << "]";
2301 
2302  m_alog->write(log::alevel::disconnect,s.str());
2303 }
2304 
2305 template <typename config>
2307 {
2308  std::stringstream s;
2309 
2310  int version = processor::get_websocket_version(m_request);
2311 
2312  // Connection Type
2313  s << "WebSocket Connection ";
2314 
2315  // Remote endpoint address & WebSocket version
2316  s << transport_con_type::get_remote_endpoint();
2317  if (version < 0) {
2318  s << " -";
2319  } else {
2320  s << " v" << version;
2321  }
2322 
2323  // User Agent
2324  std::string ua = m_request.get_header("User-Agent");
2325  if (ua.empty()) {
2326  s << " \"\" ";
2327  } else {
2328  // check if there are any quotes in the user agent
2329  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2330  }
2331 
2332  // URI
2333  s << (m_uri ? m_uri->get_resource() : "-");
2334 
2335  // HTTP Status code
2336  s << " " << m_response.get_status_code();
2337 
2338  // WebSocket++ error code & reason
2339  s << " " << m_ec << " " << m_ec.message();
2340 
2341  m_alog->write(log::alevel::fail,s.str());
2342 }
2343 
2344 template <typename config>
2346  std::stringstream s;
2347 
2348  if (processor::is_websocket_handshake(m_request)) {
2349  m_alog->write(log::alevel::devel,"Call to log_http_result for WebSocket");
2350  return;
2351  }
2352 
2353  // Connection Type
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();
2360 
2361  // User Agent
2362  std::string ua = m_request.get_header("User-Agent");
2363  if (ua.empty()) {
2364  s << " \"\" ";
2365  } else {
2366  // check if there are any quotes in the user agent
2367  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2368  }
2369 
2370  m_alog->write(log::alevel::http,s.str());
2371 }
2372 
2373 } // namespace websocketpp
2374 
2375 #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.
uri_ptr get_uri() const
Gets the connection URI.
std::string const & get_request_body() const
Retrieve a request body.
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.
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
bool terminal(value code)
Determine if the code represents an unrecoverable error.
Definition: close.hpp:217
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
std::string const & get_response_header(std::string const &key) const
Retrieve a response header.
void handle_transport_init(lib::error_code const &ec)
status_code::value m_error_code
Definition: constants.hpp:303
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
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
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:294
void select_subprotocol(std::string const &value, lib::error_code &ec)
Select a subprotocol to use (exception free)
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_subprotocol() const
Gets the negotated subprotocol.
static const int client_version
WebSocket Protocol version to use as a client.
virtual const char * what() const
Definition: constants.hpp:296
static level const disconnect
One line for each closed connection. Includes closing codes and reasons.
Definition: levels.hpp:123
size_t get_buffered_amount() const
Get the size of the outgoing write buffer (in payload bytes)
lib::shared_ptr< message > ptr
Definition: message.hpp:86
std::string const & get_host() const
Returns the host component of the connection URI.
lib::error_code resume_reading()
Resume reading of new data.
void 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)
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)
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:352
void handle_interrupt()
Transport inturrupt callback.
std::string const & get_request_header(std::string const &key) const
Retrieve a request header.
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.
std::string const & get_origin() const
Return the same origin policy origin value from the opening request.
WebSocket close handshake timed out.
Definition: error.hpp:117
Catch-all library error.
Definition: error.hpp:47
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
session::state::value get_state() const
Return the connection state.
static uint8_t const close_reason_size
Maximum size of close frame reason.
Definition: frame.hpp:169
std::vector< int > const & get_supported_versions() const
Get array of WebSocket protocol versions that this connection supports.
void set_uri(uri_ptr uri)
Sets the connection URI.
WebSocket opening handshake timed out.
Definition: error.hpp:114
uint32_t level
Type of a channel package.
Definition: levels.hpp:37
concurrency_type::scoped_lock_type scoped_lock_type
Definition: connection.hpp:262
void read_handshake(size_t num_bytes)
Attempted to use a server specific feature on a client endpoint.
Definition: error.hpp:108
bool get_secure() const
Returns the secure flag from the connection URI.
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:333
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
uint16_t get_port() const
Returns the port component of the connection URI.
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
std::vector< std::string > const & get_requested_subprotocols() const
Gets all of the subprotocols requested by the client.
void remove_header(std::string const &key)
Remove a header.
void set_body(std::string const &value)
Set response body content.