WebSocket++  0.8.3-dev
C++ websocket client/server library
connection_impl.hpp
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 
31 #include <websocketpp/processors/hybi00.hpp>
32 #include <websocketpp/processors/hybi07.hpp>
33 #include <websocketpp/processors/hybi08.hpp>
34 #include <websocketpp/processors/hybi13.hpp>
35 
36 #include <websocketpp/processors/processor.hpp>
37 
38 #include <websocketpp/common/platforms.hpp>
39 #include <websocketpp/common/system_error.hpp>
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>
55 {
57  "connection set_termination_handler");
58 
59  //scoped_lock_type lock(m_connection_state_lock);
60 
62 }
63 
64 template <typename config>
65 std::string const & connection<config>::get_origin() const {
66  //scoped_lock_type lock(m_connection_state_lock);
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>
84  frame::opcode::value op)
85 {
88  msg->set_compressed(true);
89 
90  return send(msg);
91 }
92 
93 template <typename config>
95  frame::opcode::value op)
96 {
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  {
112  if (m_state != session::state::open) {
114  }
115  }
116 
118  bool needs_writing = false;
119 
120  if (msg->get_prepared()) {
121  outgoing_msg = msg;
122 
126  } else {
128 
129  if (!outgoing_msg) {
131  }
132 
135 
136  if (ec) {
137  return ec;
138  }
139 
142  }
143 
144  if (needs_writing) {
146  &type::write_frame,
147  type::get_shared()
148  ));
149  }
150 
151  return lib::error_code();
152 }
153 
154 template <typename config>
156  if (m_alog->static_test(log::alevel::devel)) {
157  m_alog->write(log::alevel::devel,"connection ping");
158  }
159 
160  {
162  if (m_state != session::state::open) {
164  ss << "connection::ping called from invalid state " << m_state;
165  m_alog->write(log::alevel::devel,ss.str());
167  return;
168  }
169  }
170 
172  if (!msg) {
174  return;
175  }
176 
178  if (ec) {return;}
179 
180  // set ping timer if we are listening for one
182  // Cancel any existing timers
183  if (m_ping_timer) {
184  m_ping_timer->cancel();
185  }
186 
187  if (m_pong_timeout_dur > 0) {
190  lib::bind(
192  type::get_shared(),
193  payload,
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  {
209  write_push(msg);
211  }
212 
213  if (needs_writing) {
215  &type::write_frame,
216  type::get_shared()
217  ));
218  }
219 
220  ec = lib::error_code();
221 }
222 
223 template<typename config>
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) {
237  if (ec == transport::error::operation_aborted) {
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 
248  }
249 }
250 
251 template <typename config>
253  if (m_alog->static_test(log::alevel::devel)) {
254  m_alog->write(log::alevel::devel,"connection pong");
255  }
256 
257  {
259  if (m_state != session::state::open) {
261  ss << "connection::pong called from invalid state " << m_state;
262  m_alog->write(log::alevel::devel,ss.str());
264  return;
265  }
266  }
267 
269  if (!msg) {
271  return;
272  }
273 
275  if (ec) {return;}
276 
277  bool needs_writing = false;
278  {
280  write_push(msg);
282  }
283 
284  if (needs_writing) {
286  &type::write_frame,
287  type::get_shared()
288  ));
289  }
290 
291  ec = lib::error_code();
292 }
293 
294 template<typename config>
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.
314 
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 
336 /// Trigger the on_interrupt handler
337 /**
338  * This is thread safe if the transport is thread safe
339  */
340 template <typename config>
342  m_alog->write(log::alevel::devel,"connection connection::interrupt");
344  lib::bind(
346  type::get_shared()
347  )
348  );
349 }
350 
351 
352 template <typename config>
354  if (m_interrupt_handler) {
356  }
357 }
358 
359 template <typename config>
361  m_alog->write(log::alevel::devel,"connection connection::pause_reading");
363  lib::bind(
365  type::get_shared()
366  )
367  );
368 }
369 
370 /// Pause reading handler. Not safe to call directly
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");
381  lib::bind(
383  type::get_shared()
384  )
385  );
386 }
387 
388 /// Resume reading helper method. Not safe to call directly
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>
406 bool connection<config>::get_secure() const {
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>
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>
448  return m_subprotocol;
449 }
450 
451 template <typename config>
452 std::vector<std::string> const &
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(),
469  {
471  return;
472  }
473 
475 }
476 
477 template <typename config>
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 
505  value);
506 
507  if (it == m_requested_subprotocols.end()) {
509  return;
510  }
511 
513  ec = lib::error_code();
514 }
515 
516 template <typename config>
518  lib::error_code ec;
519  this->select_subprotocol(value,ec);
520  if (ec) {
521  throw exception(ec);
522  }
523 }
524 
525 
526 template <typename config>
527 std::string const &
529  return m_request.get_header(key);
530 }
531 
532 template <typename config>
533 std::string const &
535  return m_request.get_body();
536 }
537 
538 template <typename config>
539 std::string const &
541  return m_response.get_header(key);
542 }
543 
544 template <typename config>
546  lib::error_code & ec)
547 {
550  return;
551  }
553  ec = lib::error_code();
554 }
555 
556 template <typename config>
558 {
559  lib::error_code ec;
560  this->set_status(code, ec);
561  if (ec) {
562  throw exception("Call to set_status from invalid state", ec);
563  }
564 }
565 
566 template <typename config>
568  std::string const & msg, lib::error_code & ec)
569 {
572  return;
573  }
574 
576  ec = lib::error_code();
577 }
578 
579 template <typename config>
581  std::string const & msg)
582 {
583  lib::error_code ec;
584  this->set_status(code, msg);
585  if (ec) {
586  throw exception("Call to set_status from invalid state", ec);
587  }
588 }
589 
590 template <typename config>
592  lib::error_code & ec)
593 {
596  return;
597  }
598 
600 }
601 
602 template <typename config>
604  lib::error_code ec;
605  this->set_body(value, ec);
606  if (ec) {
607  throw exception("Call to set_status from invalid state", ec);
608  }
609 }
610 
611 template <typename config>
613  std::string const & val, lib::error_code & ec)
614 {
615  if (m_is_server) {
617  // we are setting response headers for an incoming server connection
619  } else {
621  }
622  } else {
624  // we are setting initial headers for an outgoing client connection
626  } else {
628  }
629  }
630 }
631 
632 template <typename config>
634  std::string const & val)
635 {
636  lib::error_code ec;
637  this->append_header(key, val, ec);
638  if (ec) {
639  throw exception("Call to append_header from invalid state", ec);
640  }
641 }
642 
643 template <typename config>
645  std::string const & val)
646 {
647  if (m_is_server) {
649  // we are setting response headers for an incoming server connection
651  } else {
653  }
654  } else {
656  // we are setting initial headers for an outgoing client connection
658  } else {
660  }
661  }
662 }
663 
664 template <typename config>
665 void connection<config>::replace_header(std::string const & key,
666  std::string const & val)
667 {
668  lib::error_code ec;
669  this->replace_header(key, val, ec);
670  if (ec) {
671  throw exception("Call to replace_header from invalid state", ec);
672  }
673 }
674 
675 template <typename config>
677 {
678  if (m_is_server) {
680  // we are setting response headers for an incoming server connection
682  } else {
684  }
685  } else {
687  // we are setting initial headers for an outgoing client connection
689  } else {
691  }
692  }
693 }
694 
695 template <typename config>
696 void connection<config>::remove_header(std::string const & key)
697 {
698  lib::error_code ec;
699  this->remove_header(key, ec);
700  if (ec) {
701  throw exception("Call to remove_header from invalid state", ec);
702  }
703 }
704 
705 /// Defer HTTP Response until later
706 /**
707  * Used in the http handler to defer the HTTP response for this connection
708  * until later. Handshake timers will be canceled and the connection will be
709  * left open until `send_http_response` or an equivalent is called.
710  *
711  * Warning: deferred connections won't time out and as a result can tie up
712  * resources.
713  *
714  * @return A status code, zero on success, non-zero otherwise
715  */
716 template <typename config>
718  // Cancel handshake timer, otherwise the connection will time out and we'll
719  // close the connection before the app has a chance to send a response.
720  if (m_handshake_timer) {
723  }
724 
725  // Do something to signal deferral
727 
728  return lib::error_code();
729 }
730 
731 /// Send deferred HTTP Response (exception free)
732 /**
733  * Sends an http response to an HTTP connection that was deferred. This will
734  * send a complete response including all headers, status line, and body
735  * text. The connection will be closed afterwards.
736  *
737  * @since 0.6.0
738  *
739  * @param ec A status code, zero on success, non-zero otherwise
740  */
741 template <typename config>
743  {
747  return;
748  }
749 
751  }
752 
754  ec = lib::error_code();
755 }
756 
757 template <typename config>
759  lib::error_code ec;
760  this->send_http_response(ec);
761  if (ec) {
762  throw exception(ec);
763  }
764 }
765 
766 
767 
768 
769 /******** logic thread ********/
770 
771 template <typename config>
773  m_alog->write(log::alevel::devel,"connection start");
774 
776  m_alog->write(log::alevel::devel,"Start called in invalid state");
778  return;
779  }
780 
782 
783  // Depending on how the transport implements init this function may return
784  // immediately and call handle_transport_init later or call
785  // handle_transport_init from this function.
787  lib::bind(
789  type::get_shared(),
791  )
792  );
793 }
794 
795 template <typename config>
797  m_alog->write(log::alevel::devel,"connection handle_transport_init");
798 
799  lib::error_code ecm = ec;
800 
803  "handle_transport_init must be called from transport init state");
805  }
806 
807  if (ecm) {
808  std::stringstream s;
809  s << "handle_transport_init received error: "<< ecm.message();
810  m_elog->write(log::elevel::rerror,s.str());
811 
812  this->terminate(ecm);
813  return;
814  }
815 
816  // At this point the transport is ready to read and write bytes.
817  if (m_is_server) {
819  this->read_handshake(1);
820  } else {
821  // We are a client. Set the processor to the version specified in the
822  // config file and send a handshake request.
825  this->send_http_request();
826  }
827 }
828 
829 template <typename config>
831  m_alog->write(log::alevel::devel,"connection read_handshake");
832 
836  lib::bind(
838  type::get_shared(),
840  )
841  );
842  }
843 
845  num_bytes,
846  m_buf,
848  lib::bind(
850  type::get_shared(),
851  lib::placeholders::_1,
853  )
854  );
855 }
856 
857 // All exit paths for this function need to call write_http_response() or submit
858 // a new read request with this function as the handler.
859 template <typename config>
862 {
863  m_alog->write(log::alevel::devel,"connection handle_read_handshake");
864 
865  lib::error_code ecm = ec;
866 
867  if (!ecm) {
869 
870  if (m_state == session::state::connecting) {
873  }
874  } else if (m_state == session::state::closed) {
875  // The connection was canceled while the response was being sent,
876  // usually by the handshake timer. This is basically expected
877  // (though hopefully rare) and there is nothing we can do so ignore.
879  "handle_read_handshake invoked after connection was closed");
880  return;
881  } else {
883  }
884  }
885 
886  if (ecm) {
887  if (ecm == transport::error::eof && m_state == session::state::closed) {
888  // we expect to get eof if the connection is closed already
890  "got (expected) eof/state error from closed con");
891  return;
892  }
893 
894  log_err(log::elevel::rerror,"handle_read_handshake",ecm);
895  this->terminate(ecm);
896  return;
897  }
898 
899  // Boundaries checking. TODO: How much of this should be done?
901  m_elog->write(log::elevel::fatal,"Fatal boundaries checking error.");
903  return;
904  }
905 
907  try {
909  } catch (http::exception &e) {
910  // All HTTP exceptions will result in this request failing and an error
911  // response being returned. No more bytes will be read in this con.
914  return;
915  }
916 
917  // More paranoid boundaries checking.
918  // TODO: Is this overkill?
920  m_elog->write(log::elevel::fatal,"Fatal boundaries checking error.");
922  return;
923  }
924 
925  if (m_alog->static_test(log::alevel::devel)) {
926  std::stringstream s;
927  s << "bytes_transferred: " << bytes_transferred
928  << " bytes, bytes processed: " << bytes_processed << " bytes";
929  m_alog->write(log::alevel::devel,s.str());
930  }
931 
932  if (m_request.ready()) {
934  if (processor_ec) {
936  return;
937  }
938 
939  if (m_processor && m_processor->get_version() == 0) {
940  // Version 00 has an extra requirement to read some bytes after the
941  // handshake
944  "Sec-WebSocket-Key3",
946  );
947  bytes_processed += 8;
948  } else {
949  // TODO: need more bytes
950  m_alog->write(log::alevel::devel,"short key3 read");
953  return;
954  }
955  }
956 
957  if (m_alog->static_test(log::alevel::devel)) {
959  if (!m_request.get_header("Sec-WebSocket-Key3").empty()) {
961  utility::to_hex(m_request.get_header("Sec-WebSocket-Key3")));
962  }
963  }
964 
965  // The remaining bytes in m_buf are frame data. Copy them to the
966  // beginning of the buffer and note the length. They will be read after
967  // the handshake completes and before more bytes are read.
970 
971 
973 
974  // We have the complete request. Process it.
976 
977  // Write a response if this is a websocket connection or if it is an
978  // HTTP connection for which the response has not been deferred or
979  // started yet by a different system (i.e. still in init state).
982  }
983  } else {
984  // The HTTP parser reported that it was not ready and wants more data.
985  // Assert that it actually consumed all the data present before overwriting
986  // the buffer. This should always be the case.
988  m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP request parser failed to consume all bytes from a read request.");
990  return;
991  }
992 
993  // read at least 1 more byte
995  1,
996  m_buf,
998  lib::bind(
1000  type::get_shared(),
1001  lib::placeholders::_1,
1002  lib::placeholders::_2
1003  )
1004  );
1005  }
1006 }
1007 
1008 // write_http_response requires the request to be fully read and the connection
1009 // to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors
1010 // before the request is fully read (specifically at a point where we aren't
1011 // sure if the hybi00 key3 bytes need to be read). This method sets the correct
1012 // state and calls write_http_response
1013 template <typename config>
1017  "write_http_response_error called in invalid state");
1019  return;
1020  }
1021 
1023 
1024  this->write_http_response(ec);
1025 }
1026 
1027 // All exit paths for this function need to call write_http_response() or submit
1028 // a new read request with this function as the handler.
1029 template <typename config>
1032 {
1033  //m_alog->write(log::alevel::devel,"connection handle_read_frame");
1034 
1035  lib::error_code ecm = ec;
1036 
1039  }
1040 
1041  if (ecm) {
1043 
1044  if (ecm == transport::error::eof) {
1045  if (m_state == session::state::closed) {
1046  // we expect to get eof if the connection is closed already
1047  // just ignore it
1048  m_alog->write(log::alevel::devel,"got eof from closed con");
1049  return;
1050  } else if (m_state == session::state::closing && !m_is_server) {
1051  // If we are a client we expect to get eof in the closing state,
1052  // this is a signal to terminate our end of the connection after
1053  // the closing handshake
1054  terminate(lib::error_code());
1055  return;
1056  }
1057  } else if (ecm == error::invalid_state) {
1058  // In general, invalid state errors in the closed state are the
1059  // result of handlers that were in the system already when the state
1060  // changed and should be ignored as they pose no problems and there
1061  // is nothing useful that we can do about them.
1062  if (m_state == session::state::closed) {
1064  "handle_read_frame: got invalid istate in closed state");
1065  return;
1066  }
1067  } else if (ecm == transport::error::action_after_shutdown) {
1068  echannel = log::elevel::info;
1069  } else {
1070  // TODO: more generally should we do something different here in the
1071  // case that m_state is cosed? Are errors after the connection is
1072  // already closed really an rerror?
1073  }
1074 
1075 
1076 
1077  log_err(echannel, "handle_read_frame", ecm);
1078  this->terminate(ecm);
1079  return;
1080  }
1081 
1082  // Boundaries checking. TODO: How much of this should be done?
1083  /*if (bytes_transferred > config::connection_read_buffer_size) {
1084  m_elog->write(log::elevel::fatal,"Fatal boundaries checking error");
1085  this->terminate(make_error_code(error::general));
1086  return;
1087  }*/
1088 
1089  size_t p = 0;
1090 
1091  if (m_alog->static_test(log::alevel::devel)) {
1092  std::stringstream s;
1093  s << "p = " << p << " bytes transferred = " << bytes_transferred;
1094  m_alog->write(log::alevel::devel,s.str());
1095  }
1096 
1097  while (p < bytes_transferred) {
1098  if (m_alog->static_test(log::alevel::devel)) {
1099  std::stringstream s;
1100  s << "calling consume with " << bytes_transferred-p << " bytes";
1101  m_alog->write(log::alevel::devel,s.str());
1102  }
1103 
1105 
1106  if (m_alog->static_test(log::alevel::devel)) {
1107  std::stringstream s;
1108  s << "Processing Bytes: " << utility::to_hex(reinterpret_cast<uint8_t*>(m_buf)+p,bytes_transferred-p);
1109  m_alog->write(log::alevel::devel,s.str());
1110  }
1111 
1112  p += m_processor->consume(
1113  reinterpret_cast<uint8_t*>(m_buf)+p,
1115  consume_ec
1116  );
1117 
1118  if (m_alog->static_test(log::alevel::devel)) {
1119  std::stringstream s;
1120  s << "bytes left after consume: " << bytes_transferred-p;
1121  m_alog->write(log::alevel::devel,s.str());
1122  }
1123  if (consume_ec) {
1124  log_err(log::elevel::rerror, "consume", consume_ec);
1125 
1127  this->terminate(consume_ec);
1128  return;
1129  } else {
1131  this->close(
1133  consume_ec.message(),
1134  close_ec
1135  );
1136 
1137  if (close_ec) {
1138  log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
1139  this->terminate(close_ec);
1140  return;
1141  }
1142  }
1143  return;
1144  }
1145 
1146  if (m_processor->ready()) {
1147  if (m_alog->static_test(log::alevel::devel)) {
1148  std::stringstream s;
1149  s << "Complete message received. Dispatching";
1150  m_alog->write(log::alevel::devel,s.str());
1151  }
1152 
1154 
1155  if (!msg) {
1156  m_alog->write(log::alevel::devel, "null message from m_processor");
1157  } else if (!is_control(msg->get_opcode())) {
1158  // data message, dispatch to user
1159  if (m_state != session::state::open) {
1160  m_elog->write(log::elevel::warn, "got non-close frame while closing");
1161  } else if (m_message_handler) {
1163  }
1164  } else {
1166  }
1167  }
1168  }
1169 
1170  read_frame();
1171 }
1172 
1173 /// Issue a new transport read unless reading is paused.
1174 template <typename config>
1176  if (!m_read_flag) {
1177  return;
1178  }
1179 
1181  // std::min wont work with undefined static const values.
1182  // TODO: is there a more elegant way to do this?
1183  // Need to determine if requesting 1 byte or the exact number of bytes
1184  // is better here. 1 byte lets us be a bit more responsive at a
1185  // potential expense of additional runs through handle_read_frame
1186  /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
1187  config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
1188  1,
1189  m_buf,
1192  );
1193 }
1194 
1195 template <typename config>
1197  m_alog->write(log::alevel::devel,"initialize_processor");
1198 
1199  // if it isn't a websocket handshake nothing to do.
1201  return lib::error_code();
1202  }
1203 
1205 
1206  if (version < 0) {
1207  m_alog->write(log::alevel::devel, "BAD REQUEST: can't determine version");
1210  }
1211 
1213 
1214  // if the processor is not null we are done
1215  if (m_processor) {
1216  return lib::error_code();
1217  }
1218 
1219  // We don't have a processor for this version. Return bad request
1220  // with Sec-WebSocket-Version header filled with values we do accept
1221  m_alog->write(log::alevel::devel, "BAD REQUEST: no processor for version");
1223 
1224  std::stringstream ss;
1225  std::string sep;
1226  std::vector<int>::const_iterator it;
1228  {
1229  ss << sep << *it;
1230  sep = ",";
1231  }
1232 
1233  m_response.replace_header("Sec-WebSocket-Version",ss.str());
1235 }
1236 
1237 template <typename config>
1239  m_alog->write(log::alevel::devel,"process handshake request");
1240 
1242  // this is not a websocket handshake. Process as plain HTTP
1243  m_alog->write(log::alevel::devel,"HTTP REQUEST");
1244 
1245  // extract URI from request
1247  m_request,
1248  (transport_con_type::is_secure() ? "https" : "http")
1249  );
1250 
1251  if (!m_uri->get_valid()) {
1252  m_alog->write(log::alevel::devel, "Bad request: failed to parse uri");
1255  }
1256 
1257  if (m_http_handler) {
1258  m_is_http = true;
1260 
1261  if (m_state == session::state::closed) {
1263  }
1264  } else {
1267  }
1268 
1269  return lib::error_code();
1270  }
1271 
1273 
1274  // Validate: make sure all required elements are present.
1275  if (ec){
1276  // Not a valid handshake request
1277  m_alog->write(log::alevel::devel, "Bad request " + ec.message());
1279  return ec;
1280  }
1281 
1282  // Read extension parameters and set up values necessary for the end user
1283  // to complete extension negotiation.
1286 
1288  // There was a fatal error in extension parsing that should result in
1289  // a failed connection attempt.
1290  m_elog->write(log::elevel::info, "Bad request: " + neg_results.first.message());
1292  return neg_results.first;
1293  } else if (neg_results.first) {
1294  // There was a fatal error in extension processing that is probably our
1295  // fault. Consider extension negotiation to have failed and continue as
1296  // if extensions were not supported
1297  m_elog->write(log::elevel::info,
1298  "Extension negotiation failed: " + neg_results.first.message());
1299  } else {
1300  // extension negotiation succeeded, set response header accordingly
1301  // we don't send an empty extensions header because it breaks many
1302  // clients.
1303  if (neg_results.second.size() > 0) {
1304  m_response.replace_header("Sec-WebSocket-Extensions",
1306  }
1307  }
1308 
1309  // extract URI from request
1311 
1312 
1313  if (!m_uri->get_valid()) {
1314  m_alog->write(log::alevel::devel, "Bad request: failed to parse uri");
1317  }
1318 
1319  // extract subprotocols
1322 
1323  if (subp_ec) {
1324  // should we do anything?
1325  }
1326 
1327  // Ask application to validate the connection
1330 
1331  // Write the appropriate response headers based on request and
1332  // processor version
1334 
1335  if (ec) {
1336  std::stringstream s;
1337  s << "Processing error: " << ec << "(" << ec.message() << ")";
1338  m_alog->write(log::alevel::devel, s.str());
1339 
1341  return ec;
1342  }
1343  } else {
1344  // User application has rejected the handshake
1345  m_alog->write(log::alevel::devel, "USER REJECT");
1346 
1347  // Use Bad Request if the user handler did not provide a more
1348  // specific http response error code.
1349  // TODO: is there a better default?
1352  }
1353 
1354  return error::make_error_code(error::rejected);
1355  }
1356 
1357  return lib::error_code();
1358 }
1359 
1360 template <typename config>
1362  m_alog->write(log::alevel::devel,"connection write_http_response");
1363 
1365  m_alog->write(log::alevel::http,"An HTTP handler took over the connection.");
1366  return;
1367  }
1368 
1372  } else {
1373  m_ec = ec;
1374  }
1375 
1376  m_response.set_version("HTTP/1.1");
1377 
1378  // Set server header based on the user agent settings
1379  if (m_response.get_header("Server").empty()) {
1380  if (!m_user_agent.empty()) {
1382  } else {
1383  m_response.remove_header("Server");
1384  }
1385  }
1386 
1387  // have the processor generate the raw bytes for the wire (if it exists)
1388  if (m_processor) {
1390  } else {
1391  // a processor wont exist for raw HTTP responses.
1393  }
1394 
1395  if (m_alog->static_test(log::alevel::devel)) {
1396  m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
1397  if (!m_response.get_header("Sec-WebSocket-Key3").empty()) {
1399  utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
1400  }
1401  }
1402 
1403  // write raw bytes
1407  lib::bind(
1409  type::get_shared(),
1410  lib::placeholders::_1
1411  )
1412  );
1413 }
1414 
1415 template <typename config>
1417  m_alog->write(log::alevel::devel,"handle_write_http_response");
1418 
1419  lib::error_code ecm = ec;
1420 
1421  if (!ecm) {
1423 
1424  if (m_state == session::state::connecting) {
1427  }
1428  } else if (m_state == session::state::closed) {
1429  // The connection was canceled while the response was being sent,
1430  // usually by the handshake timer. This is basically expected
1431  // (though hopefully rare) and there is nothing we can do so ignore.
1433  "handle_write_http_response invoked after connection was closed");
1434  return;
1435  } else {
1437  }
1438  }
1439 
1440  if (ecm) {
1441  if (ecm == transport::error::eof && m_state == session::state::closed) {
1442  // we expect to get eof if the connection is closed already
1444  "got (expected) eof/state error from closed con");
1445  return;
1446  }
1447 
1448  log_err(log::elevel::rerror,"handle_write_http_response",ecm);
1449  this->terminate(ecm);
1450  return;
1451  }
1452 
1453  if (m_handshake_timer) {
1456  }
1457 
1459  {
1460  /*if (m_processor || m_ec == error::http_parse_error ||
1461  m_ec == error::invalid_version || m_ec == error::unsupported_version
1462  || m_ec == error::upgrade_required)
1463  {*/
1464  if (!m_is_http) {
1465  std::stringstream s;
1466  s << "Handshake ended with HTTP error: "
1468  m_elog->write(log::elevel::rerror,s.str());
1469  } else {
1470  // if this was not a websocket connection, we have written
1471  // the expected response and the connection can be closed.
1472 
1473  this->log_http_result();
1474 
1475  if (m_ec) {
1477  "got to writing HTTP results with m_ec set: "+m_ec.message());
1478  }
1480  }
1481 
1482  this->terminate(m_ec);
1483  return;
1484  }
1485 
1486  this->log_open_result();
1487 
1489  m_state = session::state::open;
1490 
1491  if (m_open_handler) {
1493  }
1494 
1496 }
1497 
1498 template <typename config>
1500  m_alog->write(log::alevel::devel,"connection send_http_request");
1501 
1502  // TODO: origin header?
1503 
1504  // Have the protocol processor fill in the appropriate fields based on the
1505  // selected client version
1506  if (m_processor) {
1507  lib::error_code ec;
1510 
1511  if (ec) {
1512  log_err(log::elevel::fatal,"Internal library error: Processor",ec);
1513  return;
1514  }
1515  } else {
1516  m_elog->write(log::elevel::fatal,"Internal library error: missing processor");
1517  return;
1518  }
1519 
1520  // Unless the user has overridden the user agent, send generic WS++ UA.
1521  if (m_request.get_header("User-Agent").empty()) {
1522  if (!m_user_agent.empty()) {
1523  m_request.replace_header("User-Agent",m_user_agent);
1524  } else {
1525  m_request.remove_header("User-Agent");
1526  }
1527  }
1528 
1530 
1531  if (m_alog->static_test(log::alevel::devel)) {
1532  m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
1533  }
1534 
1535  if (m_open_handshake_timeout_dur > 0) {
1538  lib::bind(
1540  type::get_shared(),
1541  lib::placeholders::_1
1542  )
1543  );
1544  }
1545 
1549  lib::bind(
1551  type::get_shared(),
1552  lib::placeholders::_1
1553  )
1554  );
1555 }
1556 
1557 template <typename config>
1559  m_alog->write(log::alevel::devel,"handle_send_http_request");
1560 
1561  lib::error_code ecm = ec;
1562 
1563  if (!ecm) {
1565 
1566  if (m_state == session::state::connecting) {
1569  } else {
1571  }
1572  } else if (m_state == session::state::closed) {
1573  // The connection was canceled while the response was being sent,
1574  // usually by the handshake timer. This is basically expected
1575  // (though hopefully rare) and there is nothing we can do so ignore.
1577  "handle_send_http_request invoked after connection was closed");
1578  return;
1579  } else {
1581  }
1582  }
1583 
1584  if (ecm) {
1585  if (ecm == transport::error::eof && m_state == session::state::closed) {
1586  // we expect to get eof if the connection is closed already
1588  "got (expected) eof/state error from closed con");
1589  return;
1590  }
1591 
1592  log_err(log::elevel::rerror,"handle_send_http_request",ecm);
1593  this->terminate(ecm);
1594  return;
1595  }
1596 
1598  1,
1599  m_buf,
1601  lib::bind(
1603  type::get_shared(),
1604  lib::placeholders::_1,
1605  lib::placeholders::_2
1606  )
1607  );
1608 }
1609 
1610 template <typename config>
1613 {
1614  m_alog->write(log::alevel::devel,"handle_read_http_response");
1615 
1616  lib::error_code ecm = ec;
1617 
1618  if (!ecm) {
1620 
1621  if (m_state == session::state::connecting) {
1624  }
1625  } else if (m_state == session::state::closed) {
1626  // The connection was canceled while the response was being sent,
1627  // usually by the handshake timer. This is basically expected
1628  // (though hopefully rare) and there is nothing we can do so ignore.
1630  "handle_read_http_response invoked after connection was closed");
1631  return;
1632  } else {
1634  }
1635  }
1636 
1637  if (ecm) {
1638  if (ecm == transport::error::eof && m_state == session::state::closed) {
1639  // we expect to get eof if the connection is closed already
1641  "got (expected) eof/state error from closed con");
1642  return;
1643  }
1644 
1645  log_err(log::elevel::rerror,"handle_read_http_response",ecm);
1646  this->terminate(ecm);
1647  return;
1648  }
1649 
1650  size_t bytes_processed = 0;
1651  // TODO: refactor this to use error codes rather than exceptions
1652  try {
1654  } catch (http::exception & e) {
1656  std::string("error in handle_read_http_response: ")+e.what());
1658  return;
1659  }
1660 
1661  m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
1662 
1663  if (m_response.headers_ready()) {
1664  if (m_handshake_timer) {
1667  }
1668 
1670  m_request,
1671  m_response
1672  );
1673  if (validate_ec) {
1674  log_err(log::elevel::rerror,"Server handshake response",validate_ec);
1675  this->terminate(validate_ec);
1676  return;
1677  }
1678 
1679  // Read extension parameters and set up values necessary for the end
1680  // user to complete extension negotiation.
1683 
1684  if (neg_results.first) {
1685  // There was a fatal error in extension negotiation. For the moment
1686  // kill all connections that fail extension negotiation.
1687 
1688  // TODO: deal with cases where the response is well formed but
1689  // doesn't match the options requested by the client. Its possible
1690  // that the best behavior in this cases is to log and continue with
1691  // an unextended connection.
1692  m_alog->write(log::alevel::devel, "Extension negotiation failed: "
1693  + neg_results.first.message());
1695  // TODO: close connection with reason 1010 (and list extensions)
1696  }
1697 
1698  // response is valid, connection can now be assumed to be open
1700  m_state = session::state::open;
1701 
1702  this->log_open_result();
1703 
1704  if (m_open_handler) {
1706  }
1707 
1708  // The remaining bytes in m_buf are frame data. Copy them to the
1709  // beginning of the buffer and note the length. They will be read after
1710  // the handshake completes and before more bytes are read.
1713 
1715  } else {
1716  // The HTTP parser reported that it was not ready and wants more data.
1717  // Assert that it actually consumed all the data present before overwriting
1718  // the buffer. This should always be the case.
1720  m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP response parser failed to consume all bytes from a read request.");
1722  return;
1723  }
1724 
1726  1,
1727  m_buf,
1729  lib::bind(
1731  type::get_shared(),
1732  lib::placeholders::_1,
1733  lib::placeholders::_2
1734  )
1735  );
1736  }
1737 }
1738 
1739 template <typename config>
1741  lib::error_code const & ec)
1742 {
1743  if (ec == transport::error::operation_aborted) {
1744  m_alog->write(log::alevel::devel,"open handshake timer cancelled");
1745  } else if (ec) {
1747  "open handle_open_handshake_timeout error: "+ec.message());
1748  // TODO: ignore or fail here?
1749  } else {
1750  m_alog->write(log::alevel::devel,"open handshake timer expired");
1752  }
1753 }
1754 
1755 template <typename config>
1757  lib::error_code const & ec)
1758 {
1759  if (ec == transport::error::operation_aborted) {
1760  m_alog->write(log::alevel::devel,"asio close handshake timer cancelled");
1761  } else if (ec) {
1763  "asio open handle_close_handshake_timeout error: "+ec.message());
1764  // TODO: ignore or fail here?
1765  } else {
1766  m_alog->write(log::alevel::devel, "asio close handshake timer expired");
1768  }
1769 }
1770 
1771 template <typename config>
1772 void connection<config>::terminate(lib::error_code const & ec) {
1773  if (m_alog->static_test(log::alevel::devel)) {
1774  m_alog->write(log::alevel::devel,"connection terminate");
1775  }
1776 
1777  // Cancel close handshake timer
1778  if (m_handshake_timer) {
1781  }
1782 
1784  if (ec) {
1785  m_ec = ec;
1788  }
1789 
1790  // TODO: does any of this need a mutex?
1791  if (m_is_http) {
1793  }
1794  if (m_state == session::state::connecting) {
1796  tstat = failed;
1797 
1798  // Log fail result here before socket is shut down and we can't get
1799  // the remote address, etc anymore
1800  if (m_ec != error::http_connection_ended) {
1801  log_fail_result();
1802  }
1803  } else if (m_state != session::state::closed) {
1805  tstat = closed;
1806  } else {
1808  "terminate called on connection that was already terminated");
1809  return;
1810  }
1811 
1812  // TODO: choose between shutdown and close based on error code sent
1813 
1815  lib::bind(
1817  type::get_shared(),
1818  tstat,
1819  lib::placeholders::_1
1820  )
1821  );
1822 }
1823 
1824 template <typename config>
1826  lib::error_code const & ec)
1827 {
1828  if (m_alog->static_test(log::alevel::devel)) {
1829  m_alog->write(log::alevel::devel,"connection handle_terminate");
1830  }
1831 
1832  if (ec) {
1833  // there was an error actually shutting down the connection
1834  log_err(log::elevel::devel,"handle_terminate",ec);
1835  }
1836 
1837  // clean shutdown
1838  if (tstat == failed) {
1839  if (m_ec != error::http_connection_ended) {
1840  if (m_fail_handler) {
1842  }
1843  }
1844  } else if (tstat == closed) {
1845  if (m_close_handler) {
1847  }
1848  log_close_result();
1849  } else {
1850  m_elog->write(log::elevel::rerror,"Unknown terminate_status");
1851  }
1852 
1853  // call the termination handler if it exists
1854  // if it exists it might (but shouldn't) refer to a bad memory location.
1855  // If it does, we don't care and should catch and ignore it.
1856  if (m_termination_handler) {
1857  try {
1859  } catch (std::exception const & e) {
1860  m_elog->write(log::elevel::warn,
1861  std::string("termination_handler call failed. Reason was: ")+e.what());
1862  }
1863  }
1864 }
1865 
1866 template <typename config>
1868  //m_alog->write(log::alevel::devel,"connection write_frame");
1869 
1870  {
1872 
1873  // Check the write flag. If true, there is an outstanding transport
1874  // write already. In this case we just return. The write handler will
1875  // start a new write if the write queue isn't empty. If false, we set
1876  // the write flag and proceed to initiate a transport write.
1877  if (m_write_flag) {
1878  return;
1879  }
1880 
1881  // pull off all the messages that are ready to write.
1882  // stop if we get a message marked terminal
1884  while (next_message) {
1886  if (!next_message->get_terminal()) {
1887  next_message = write_pop();
1888  } else {
1890  }
1891  }
1892 
1893  if (m_current_msgs.empty()) {
1894  // there was nothing to send
1895  return;
1896  } else {
1897  // At this point we own the next messages to be sent and are
1898  // responsible for holding the write flag until they are
1899  // successfully sent or there is some error
1900  m_write_flag = true;
1901  }
1902  }
1903 
1904  typename std::vector<message_ptr>::iterator it;
1905  for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1906  std::string const & header = (*it)->get_header();
1907  std::string const & payload = (*it)->get_payload();
1908 
1911  }
1912 
1913  // Print detailed send stats if those log levels are enabled
1917 
1918  general << "Dispatching write containing " << m_current_msgs.size()
1919  <<" message(s) containing ";
1920  header << "Header Bytes: \n";
1921  payload << "Payload Bytes: \n";
1922 
1923  size_t hbytes = 0;
1924  size_t pbytes = 0;
1925 
1926  for (size_t i = 0; i < m_current_msgs.size(); i++) {
1929 
1930 
1931  header << "[" << i << "] ("
1932  << m_current_msgs[i]->get_header().size() << ") "
1933  << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
1934 
1937  payload << "[" << i << "] ("
1938  << m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
1939  << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1942  )
1943  << "\n";
1944  }
1945  }
1946  }
1947 
1948  general << hbytes << " header bytes and " << pbytes << " payload bytes";
1949 
1953  }
1954  }
1955 
1957  m_send_buffer,
1959  );
1960 }
1961 
1962 template <typename config>
1964 {
1965  if (m_alog->static_test(log::alevel::devel)) {
1966  m_alog->write(log::alevel::devel,"connection handle_write_frame");
1967  }
1968 
1970 
1971  m_send_buffer.clear();
1973  // TODO: recycle instead of deleting
1974 
1975  if (ec) {
1976  log_err(log::elevel::fatal,"handle_write_frame",ec);
1977  this->terminate(ec);
1978  return;
1979  }
1980 
1981  if (terminal) {
1982  this->terminate(lib::error_code());
1983  return;
1984  }
1985 
1986  bool needs_writing = false;
1987  {
1989 
1990  // release write flag
1991  m_write_flag = false;
1992 
1994  }
1995 
1996  if (needs_writing) {
1998  &type::write_frame,
1999  type::get_shared()
2000  ));
2001  }
2002 }
2003 
2004 template <typename config>
2006 {
2007  return versions_supported;
2008 }
2009 
2010 template <typename config>
2012 {
2013  m_alog->write(log::alevel::devel,"process_control_frame");
2014 
2015  frame::opcode::value op = msg->get_opcode();
2016  lib::error_code ec;
2017 
2018  std::stringstream s;
2019  s << "Control frame received with opcode " << op;
2020  m_alog->write(log::alevel::control,s.str());
2021 
2022  if (m_state == session::state::closed) {
2023  m_elog->write(log::elevel::warn,"got frame in state closed");
2024  return;
2025  }
2026  if (op != frame::opcode::CLOSE && m_state != session::state::open) {
2027  m_elog->write(log::elevel::warn,"got non-close frame in state closing");
2028  return;
2029  }
2030 
2031  if (op == frame::opcode::PING) {
2032  bool should_reply = true;
2033 
2034  if (m_ping_handler) {
2036  }
2037 
2038  if (should_reply) {
2039  this->pong(msg->get_payload(),ec);
2040  if (ec) {
2041  log_err(log::elevel::devel,"Failed to send response pong",ec);
2042  }
2043  }
2044  } else if (op == frame::opcode::PONG) {
2045  if (m_ping_timer) {
2046  m_ping_timer->cancel();
2047  }
2048  if (m_pong_handler) {
2050  }
2051  } else if (op == frame::opcode::CLOSE) {
2052  m_alog->write(log::alevel::devel,"got close frame");
2053  // record close code and reason somewhere
2054 
2056  if (ec) {
2057  s.str("");
2059  s << "Received invalid close code " << m_remote_close_code
2060  << " dropping connection per config.";
2061  m_elog->write(log::elevel::devel,s.str());
2062  this->terminate(ec);
2063  } else {
2064  s << "Received invalid close code " << m_remote_close_code
2065  << " sending acknowledgement and closing";
2066  m_elog->write(log::elevel::devel,s.str());
2068  "Invalid close code");
2069  if (ec) {
2070  log_err(log::elevel::devel,"send_close_ack",ec);
2071  }
2072  }
2073  return;
2074  }
2075 
2077  if (ec) {
2080  "Received invalid close reason. Dropping connection per config");
2081  this->terminate(ec);
2082  } else {
2084  "Received invalid close reason. Sending acknowledgement and closing");
2086  "Invalid close reason");
2087  if (ec) {
2088  log_err(log::elevel::devel,"send_close_ack",ec);
2089  }
2090  }
2091  return;
2092  }
2093 
2094  if (m_state == session::state::open) {
2095  s.str("");
2096  s << "Received close frame with code " << m_remote_close_code
2097  << " and reason " << m_remote_close_reason;
2098  m_alog->write(log::alevel::devel,s.str());
2099 
2100  ec = send_close_ack();
2101  if (ec) {
2102  log_err(log::elevel::devel,"send_close_ack",ec);
2103  }
2104  } else if (m_state == session::state::closing && !m_was_clean) {
2105  // ack of our close
2106  m_alog->write(log::alevel::devel, "Got acknowledgement of close");
2107 
2108  m_was_clean = true;
2109 
2110  // If we are a server terminate the connection now. Clients should
2111  // leave the connection open to give the server an opportunity to
2112  // initiate the TCP close. The client's timer will handle closing
2113  // its side of the connection if the server misbehaves.
2114  //
2115  // TODO: different behavior if the underlying transport doesn't
2116  // support timers?
2117  if (m_is_server) {
2118  terminate(lib::error_code());
2119  }
2120  } else {
2121  // spurious, ignore
2122  m_elog->write(log::elevel::devel, "Got close frame in wrong state");
2123  }
2124  } else {
2125  // got an invalid control opcode
2126  m_elog->write(log::elevel::devel, "Got control frame with invalid opcode");
2127  // initiate protocol error shutdown
2128  }
2129 }
2130 
2131 template <typename config>
2133  std::string const & reason)
2134 {
2135  return send_close_frame(code,reason,true,m_is_server);
2136 }
2137 
2138 template <typename config>
2140  std::string const & reason, bool ack, bool terminal)
2141 {
2142  m_alog->write(log::alevel::devel,"send_close_frame");
2143 
2144  // check for special codes
2145 
2146  // If silent close is set, respect it and blank out close information
2147  // Otherwise use whatever has been specified in the parameters. If
2148  // parameters specifies close::status::blank then determine what to do
2149  // based on whether or not this is an ack. If it is not an ack just
2150  // send blank info. If it is an ack then echo the close information from
2151  // the remote endpoint.
2152  if (config::silent_close) {
2153  m_alog->write(log::alevel::devel,"closing silently");
2156  } else if (code != close::status::blank) {
2157  m_alog->write(log::alevel::devel,"closing with specified codes");
2160  } else if (!ack) {
2161  m_alog->write(log::alevel::devel,"closing with no status code");
2164  } else if (m_remote_close_code == close::status::no_status) {
2166  "acknowledging a no-status close with normal code");
2169  } else {
2170  m_alog->write(log::alevel::devel,"acknowledging with remote codes");
2173  }
2174 
2175  std::stringstream s;
2176  s << "Closing with code: " << m_local_close_code << ", and reason: "
2178  m_alog->write(log::alevel::devel,s.str());
2179 
2181  if (!msg) {
2183  }
2184 
2187  if (ec) {
2188  return ec;
2189  }
2190 
2191  // Messages flagged terminal will result in the TCP connection being dropped
2192  // after the message has been written. This is typically used when servers
2193  // send an ack and when any endpoint encounters a protocol error
2194  if (terminal) {
2195  msg->set_terminal(true);
2196  }
2197 
2199 
2200  if (ack) {
2201  m_was_clean = true;
2202  }
2203 
2204  // Start a timer so we don't wait forever for the acknowledgement close
2205  // frame
2209  lib::bind(
2211  type::get_shared(),
2212  lib::placeholders::_1
2213  )
2214  );
2215  }
2216 
2217  bool needs_writing = false;
2218  {
2220  write_push(msg);
2222  }
2223 
2224  if (needs_writing) {
2226  &type::write_frame,
2227  type::get_shared()
2228  ));
2229  }
2230 
2231  return lib::error_code();
2232 }
2233 
2234 template <typename config>
2235 typename connection<config>::processor_ptr
2236 connection<config>::get_processor(int version) const {
2237  // TODO: allow disabling certain versions
2238 
2239  processor_ptr p;
2240 
2241  switch (version) {
2242  case 0:
2245  m_is_server,
2247  );
2248  break;
2249  case 7:
2252  m_is_server,
2253  m_msg_manager,
2254  lib::ref(m_rng)
2255  );
2256  break;
2257  case 8:
2260  m_is_server,
2261  m_msg_manager,
2262  lib::ref(m_rng)
2263  );
2264  break;
2265  case 13:
2268  m_is_server,
2269  m_msg_manager,
2270  lib::ref(m_rng)
2271  );
2272  break;
2273  default:
2274  return p;
2275  }
2276 
2277  // Settings not configured by the constructor
2279 
2280  return p;
2281 }
2282 
2283 template <typename config>
2285 {
2286  if (!msg) {
2287  return;
2288  }
2289 
2292 
2293  if (m_alog->static_test(log::alevel::devel)) {
2294  std::stringstream s;
2295  s << "write_push: message count: " << m_send_queue.size()
2296  << " buffer size: " << m_send_buffer_size;
2297  m_alog->write(log::alevel::devel,s.str());
2298  }
2299 }
2300 
2301 template <typename config>
2303 {
2304  message_ptr msg;
2305 
2306  if (m_send_queue.empty()) {
2307  return msg;
2308  }
2309 
2310  msg = m_send_queue.front();
2311 
2313  m_send_queue.pop();
2314 
2315  if (m_alog->static_test(log::alevel::devel)) {
2316  std::stringstream s;
2317  s << "write_pop: message count: " << m_send_queue.size()
2318  << " buffer size: " << m_send_buffer_size;
2319  m_alog->write(log::alevel::devel,s.str());
2320  }
2321  return msg;
2322 }
2323 
2324 template <typename config>
2326 {
2327  std::stringstream s;
2328 
2329  int version;
2331  version = -1;
2332  } else {
2334  }
2335 
2336  // Connection Type
2337  s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
2338 
2339  // Remote endpoint address
2341 
2342  // Version string if WebSocket
2343  if (version != -1) {
2344  s << "v" << version << " ";
2345  }
2346 
2347  // User Agent
2348  std::string ua = m_request.get_header("User-Agent");
2349  if (ua.empty()) {
2350  s << "\"\" ";
2351  } else {
2352  // check if there are any quotes in the user agent
2353  s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2354  }
2355 
2356  // URI
2357  s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
2358 
2359  // Status code
2361 
2362  m_alog->write(log::alevel::connect,s.str());
2363 }
2364 
2365 template <typename config>
2367 {
2368  std::stringstream s;
2369 
2370  s << "Disconnect "
2371  << "close local:[" << m_local_close_code
2373  << "] remote:[" << m_remote_close_code
2374  << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]";
2375 
2377 }
2378 
2379 template <typename config>
2381 {
2382  std::stringstream s;
2383 
2385 
2386  // Connection Type
2387  s << "WebSocket Connection ";
2388 
2389  // Remote endpoint address & WebSocket version
2391  if (version < 0) {
2392  s << " -";
2393  } else {
2394  s << " v" << version;
2395  }
2396 
2397  // User Agent
2398  std::string ua = m_request.get_header("User-Agent");
2399  if (ua.empty()) {
2400  s << " \"\" ";
2401  } else {
2402  // check if there are any quotes in the user agent
2403  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2404  }
2405 
2406  // URI
2407  s << (m_uri ? m_uri->get_resource() : "-");
2408 
2409  // HTTP Status code
2410  s << " " << m_response.get_status_code();
2411 
2412  // WebSocket++ error code & reason
2413  s << " " << m_ec << " " << m_ec.message();
2414 
2415  m_alog->write(log::alevel::fail,s.str());
2416 }
2417 
2418 template <typename config>
2420  std::stringstream s;
2421 
2423  m_alog->write(log::alevel::devel,"Call to log_http_result for WebSocket");
2424  return;
2425  }
2426 
2427  // Connection Type
2428  s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host"))
2430  << " \"" << m_request.get_method()
2431  << " " << (m_uri ? m_uri->get_resource() : "-")
2432  << " " << m_request.get_version() << "\" " << m_response.get_status_code()
2433  << " " << m_response.get_body().size();
2434 
2435  // User Agent
2436  std::string ua = m_request.get_header("User-Agent");
2437  if (ua.empty()) {
2438  s << " \"\" ";
2439  } else {
2440  // check if there are any quotes in the user agent
2441  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2442  }
2443 
2444  m_alog->write(log::alevel::http,s.str());
2445 }
2446 
2447 } // namespace websocketpp
2448 
2449 #endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
websocketpp::versions_supported
static std::vector< int > const versions_supported(helper, helper+4)
Container that stores the list of protocol versions supported.