WebSocket++  0.7.0
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>
53 void connection<config>::set_termination_handler(
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;
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;
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 }
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 &
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 &
540  return m_response.get_header(key);
541 }
542 
543 // TODO: EXCEPTION_FREE
544 template <typename config>
546 {
548  throw exception("Call to set_status from invalid state",
550  }
552 }
553 
554 // TODO: EXCEPTION_FREE
555 template <typename config>
557  std::string const & msg)
558 {
560  throw exception("Call to set_status from invalid state",
562  }
563 
565 }
566 
567 // TODO: EXCEPTION_FREE
568 template <typename config>
571  throw exception("Call to set_status from invalid state",
573  }
574 
576 }
577 
578 // TODO: EXCEPTION_FREE
579 template <typename config>
581  std::string const & val)
582 {
583  if (m_is_server) {
585  // we are setting response headers for an incoming server connection
587  } else {
588  throw exception("Call to append_header from invalid state",
590  }
591  } else {
593  // we are setting initial headers for an outgoing client connection
595  } else {
596  throw exception("Call to append_header from invalid state",
598  }
599  }
600 }
601 
602 // TODO: EXCEPTION_FREE
603 template <typename config>
605  std::string const & val)
606 {
607  if (m_is_server) {
609  // we are setting response headers for an incoming server connection
611  } else {
612  throw exception("Call to replace_header from invalid state",
614  }
615  } else {
617  // we are setting initial headers for an outgoing client connection
619  } else {
620  throw exception("Call to replace_header from invalid state",
622  }
623  }
624 }
625 
626 // TODO: EXCEPTION_FREE
627 template <typename config>
629 {
630  if (m_is_server) {
632  // we are setting response headers for an incoming server connection
634  } else {
635  throw exception("Call to remove_header from invalid state",
637  }
638  } else {
640  // we are setting initial headers for an outgoing client connection
642  } else {
643  throw exception("Call to remove_header from invalid state",
645  }
646  }
647 }
648 
649 /// Defer HTTP Response until later
650 /**
651  * Used in the http handler to defer the HTTP response for this connection
652  * until later. Handshake timers will be canceled and the connection will be
653  * left open until `send_http_response` or an equivalent is called.
654  *
655  * Warning: deferred connections won't time out and as a result can tie up
656  * resources.
657  *
658  * @return A status code, zero on success, non-zero otherwise
659  */
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) {
667  }
668 
669  // Do something to signal deferral
671 
672  return lib::error_code();
673 }
674 
675 /// Send deferred HTTP Response (exception free)
676 /**
677  * Sends an http response to an HTTP connection that was deferred. This will
678  * send a complete response including all headers, status line, and body
679  * text. The connection will be closed afterwards.
680  *
681  * @since 0.6.0
682  *
683  * @param ec A status code, zero on success, non-zero otherwise
684  */
685 template <typename config>
687  {
691  return;
692  }
693 
695  }
696 
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 
720  m_alog.write(log::alevel::devel,"Start called in invalid state");
722  return;
723  }
724 
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(
733  type::get_shared(),
735  )
736  );
737 }
738 
739 template <typename config>
741  m_alog.write(log::alevel::devel,"connection handle_transport_init");
742 
743  lib::error_code ecm = ec;
744 
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();
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) {
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.
769  this->send_http_request();
770  }
771 }
772 
773 template <typename config>
775  m_alog.write(log::alevel::devel,"connection read_handshake");
776 
780  lib::bind(
782  type::get_shared(),
784  )
785  );
786  }
787 
789  num_bytes,
790  m_buf,
792  lib::bind(
794  type::get_shared(),
795  lib::placeholders::_1,
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>
806 {
807  m_alog.write(log::alevel::devel,"connection handle_read_handshake");
808 
809  lib::error_code ecm = ec;
810 
811  if (!ecm) {
813 
814  if (m_state == session::state::connecting) {
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.
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
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?
845  m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
847  return;
848  }
849 
851  try {
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.
858  return;
859  }
860 
861  // More paranoid boundaries checking.
862  // TODO: Is this overkill?
864  m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
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";
874  }
875 
876  if (m_request.ready()) {
878  if (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
888  "Sec-WebSocket-Key3",
890  );
891  bytes_processed += 8;
892  } else {
893  // TODO: need more bytes
894  m_alog.write(log::alevel::devel,"short key3 read");
897  return;
898  }
899  }
900 
901  if (m_alog.static_test(log::alevel::devel)) {
903  if (!m_request.get_header("Sec-WebSocket-Key3").empty()) {
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.
914 
915 
917 
918  // We have the complete request. Process it.
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).
926  }
927  } else {
928  // read at least 1 more byte
930  1,
931  m_buf,
933  lib::bind(
935  type::get_shared(),
936  lib::placeholders::_1,
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>
952  "write_http_response_error called in invalid state");
954  return;
955  }
956 
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>
967 {
968  //m_alog.write(log::alevel::devel,"connection handle_read_frame");
969 
970  lib::error_code ecm = ec;
971 
974  }
975 
976  if (ecm) {
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
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) {
999  "handle_read_frame: got invalid istate in closed state");
1000  return;
1001  }
1002  } else if (ecm == transport::error::tls_short_read) {
1003  if (m_state == session::state::closed) {
1004  // We expect to get a TLS short read if we try to read after the
1005  // connection is closed. If this happens ignore and exit the
1006  // read frame path.
1007  terminate(lib::error_code());
1008  return;
1009  }
1010  echannel = log::elevel::rerror;
1011  } else if (ecm == transport::error::action_after_shutdown) {
1012  echannel = log::elevel::info;
1013  }
1014 
1015  log_err(echannel, "handle_read_frame", ecm);
1016  this->terminate(ecm);
1017  return;
1018  }
1019 
1020  // Boundaries checking. TODO: How much of this should be done?
1021  /*if (bytes_transferred > config::connection_read_buffer_size) {
1022  m_elog.write(log::elevel::fatal,"Fatal boundaries checking error");
1023  this->terminate(make_error_code(error::general));
1024  return;
1025  }*/
1026 
1027  size_t p = 0;
1028 
1029  if (m_alog.static_test(log::alevel::devel)) {
1030  std::stringstream s;
1031  s << "p = " << p << " bytes transferred = " << bytes_transferred;
1032  m_alog.write(log::alevel::devel,s.str());
1033  }
1034 
1035  while (p < bytes_transferred) {
1036  if (m_alog.static_test(log::alevel::devel)) {
1037  std::stringstream s;
1038  s << "calling consume with " << bytes_transferred-p << " bytes";
1039  m_alog.write(log::alevel::devel,s.str());
1040  }
1041 
1043 
1044  if (m_alog.static_test(log::alevel::devel)) {
1045  std::stringstream s;
1046  s << "Processing Bytes: " << utility::to_hex(reinterpret_cast<uint8_t*>(m_buf)+p,bytes_transferred-p);
1047  m_alog.write(log::alevel::devel,s.str());
1048  }
1049 
1050  p += m_processor->consume(
1051  reinterpret_cast<uint8_t*>(m_buf)+p,
1053  consume_ec
1054  );
1055 
1056  if (m_alog.static_test(log::alevel::devel)) {
1057  std::stringstream s;
1058  s << "bytes left after consume: " << bytes_transferred-p;
1059  m_alog.write(log::alevel::devel,s.str());
1060  }
1061  if (consume_ec) {
1062  log_err(log::elevel::rerror, "consume", consume_ec);
1063 
1065  this->terminate(consume_ec);
1066  return;
1067  } else {
1069  this->close(
1071  consume_ec.message(),
1072  close_ec
1073  );
1074 
1075  if (close_ec) {
1076  log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
1077  this->terminate(close_ec);
1078  return;
1079  }
1080  }
1081  return;
1082  }
1083 
1084  if (m_processor->ready()) {
1085  if (m_alog.static_test(log::alevel::devel)) {
1086  std::stringstream s;
1087  s << "Complete message received. Dispatching";
1088  m_alog.write(log::alevel::devel,s.str());
1089  }
1090 
1092 
1093  if (!msg) {
1094  m_alog.write(log::alevel::devel, "null message from m_processor");
1095  } else if (!is_control(msg->get_opcode())) {
1096  // data message, dispatch to user
1097  if (m_state != session::state::open) {
1098  m_elog.write(log::elevel::warn, "got non-close frame while closing");
1099  } else if (m_message_handler) {
1101  }
1102  } else {
1104  }
1105  }
1106  }
1107 
1108  read_frame();
1109 }
1110 
1111 /// Issue a new transport read unless reading is paused.
1112 template <typename config>
1114  if (!m_read_flag) {
1115  return;
1116  }
1117 
1119  // std::min wont work with undefined static const values.
1120  // TODO: is there a more elegant way to do this?
1121  // Need to determine if requesting 1 byte or the exact number of bytes
1122  // is better here. 1 byte lets us be a bit more responsive at a
1123  // potential expense of additional runs through handle_read_frame
1124  /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
1125  config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
1126  1,
1127  m_buf,
1130  );
1131 }
1132 
1133 template <typename config>
1135  m_alog.write(log::alevel::devel,"initialize_processor");
1136 
1137  // if it isn't a websocket handshake nothing to do.
1139  return lib::error_code();
1140  }
1141 
1143 
1144  if (version < 0) {
1145  m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
1148  }
1149 
1151 
1152  // if the processor is not null we are done
1153  if (m_processor) {
1154  return lib::error_code();
1155  }
1156 
1157  // We don't have a processor for this version. Return bad request
1158  // with Sec-WebSocket-Version header filled with values we do accept
1159  m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version");
1161 
1162  std::stringstream ss;
1163  std::string sep;
1164  std::vector<int>::const_iterator it;
1166  {
1167  ss << sep << *it;
1168  sep = ",";
1169  }
1170 
1171  m_response.replace_header("Sec-WebSocket-Version",ss.str());
1173 }
1174 
1175 template <typename config>
1177  m_alog.write(log::alevel::devel,"process handshake request");
1178 
1180  // this is not a websocket handshake. Process as plain HTTP
1181  m_alog.write(log::alevel::devel,"HTTP REQUEST");
1182 
1183  // extract URI from request
1185  m_request,
1186  (transport_con_type::is_secure() ? "https" : "http")
1187  );
1188 
1189  if (!m_uri->get_valid()) {
1190  m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
1193  }
1194 
1195  if (m_http_handler) {
1196  m_is_http = true;
1198 
1199  if (m_state == session::state::closed) {
1201  }
1202  } else {
1205  }
1206 
1207  return lib::error_code();
1208  }
1209 
1211 
1212  // Validate: make sure all required elements are present.
1213  if (ec){
1214  // Not a valid handshake request
1215  m_alog.write(log::alevel::devel, "Bad request " + ec.message());
1217  return ec;
1218  }
1219 
1220  // Read extension parameters and set up values necessary for the end user
1221  // to complete extension negotiation.
1224 
1226  // There was a fatal error in extension parsing that should result in
1227  // a failed connection attempt.
1228  m_elog.write(log::elevel::info, "Bad request: " + neg_results.first.message());
1230  return neg_results.first;
1231  } else if (neg_results.first) {
1232  // There was a fatal error in extension processing that is probably our
1233  // fault. Consider extension negotiation to have failed and continue as
1234  // if extensions were not supported
1236  "Extension negotiation failed: " + neg_results.first.message());
1237  } else {
1238  // extension negotiation succeeded, set response header accordingly
1239  // we don't send an empty extensions header because it breaks many
1240  // clients.
1241  if (neg_results.second.size() > 0) {
1242  m_response.replace_header("Sec-WebSocket-Extensions",
1244  }
1245  }
1246 
1247  // extract URI from request
1249 
1250 
1251  if (!m_uri->get_valid()) {
1252  m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
1255  }
1256 
1257  // extract subprotocols
1260 
1261  if (subp_ec) {
1262  // should we do anything?
1263  }
1264 
1265  // Ask application to validate the connection
1268 
1269  // Write the appropriate response headers based on request and
1270  // processor version
1272 
1273  if (ec) {
1274  std::stringstream s;
1275  s << "Processing error: " << ec << "(" << ec.message() << ")";
1276  m_alog.write(log::alevel::devel, s.str());
1277 
1279  return ec;
1280  }
1281  } else {
1282  // User application has rejected the handshake
1283  m_alog.write(log::alevel::devel, "USER REJECT");
1284 
1285  // Use Bad Request if the user handler did not provide a more
1286  // specific http response error code.
1287  // TODO: is there a better default?
1290  }
1291 
1292  return error::make_error_code(error::rejected);
1293  }
1294 
1295  return lib::error_code();
1296 }
1297 
1298 template <typename config>
1300  m_alog.write(log::alevel::devel,"connection write_http_response");
1301 
1303  m_alog.write(log::alevel::http,"An HTTP handler took over the connection.");
1304  return;
1305  }
1306 
1310  } else {
1311  m_ec = ec;
1312  }
1313 
1314  m_response.set_version("HTTP/1.1");
1315 
1316  // Set server header based on the user agent settings
1317  if (m_response.get_header("Server").empty()) {
1318  if (!m_user_agent.empty()) {
1320  } else {
1321  m_response.remove_header("Server");
1322  }
1323  }
1324 
1325  // have the processor generate the raw bytes for the wire (if it exists)
1326  if (m_processor) {
1328  } else {
1329  // a processor wont exist for raw HTTP responses.
1331  }
1332 
1333  if (m_alog.static_test(log::alevel::devel)) {
1334  m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
1335  if (!m_response.get_header("Sec-WebSocket-Key3").empty()) {
1337  utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
1338  }
1339  }
1340 
1341  // write raw bytes
1345  lib::bind(
1347  type::get_shared(),
1348  lib::placeholders::_1
1349  )
1350  );
1351 }
1352 
1353 template <typename config>
1355  m_alog.write(log::alevel::devel,"handle_write_http_response");
1356 
1357  lib::error_code ecm = ec;
1358 
1359  if (!ecm) {
1361 
1362  if (m_state == session::state::connecting) {
1365  }
1366  } else if (m_state == session::state::closed) {
1367  // The connection was canceled while the response was being sent,
1368  // usually by the handshake timer. This is basically expected
1369  // (though hopefully rare) and there is nothing we can do so ignore.
1371  "handle_write_http_response invoked after connection was closed");
1372  return;
1373  } else {
1375  }
1376  }
1377 
1378  if (ecm) {
1379  if (ecm == transport::error::eof && m_state == session::state::closed) {
1380  // we expect to get eof if the connection is closed already
1382  "got (expected) eof/state error from closed con");
1383  return;
1384  }
1385 
1386  log_err(log::elevel::rerror,"handle_write_http_response",ecm);
1387  this->terminate(ecm);
1388  return;
1389  }
1390 
1391  if (m_handshake_timer) {
1394  }
1395 
1397  {
1398  /*if (m_processor || m_ec == error::http_parse_error ||
1399  m_ec == error::invalid_version || m_ec == error::unsupported_version
1400  || m_ec == error::upgrade_required)
1401  {*/
1402  if (!m_is_http) {
1403  std::stringstream s;
1404  s << "Handshake ended with HTTP error: "
1406  m_elog.write(log::elevel::rerror,s.str());
1407  } else {
1408  // if this was not a websocket connection, we have written
1409  // the expected response and the connection can be closed.
1410 
1411  this->log_http_result();
1412 
1413  if (m_ec) {
1415  "got to writing HTTP results with m_ec set: "+m_ec.message());
1416  }
1418  }
1419 
1420  this->terminate(m_ec);
1421  return;
1422  }
1423 
1424  this->log_open_result();
1425 
1427  m_state = session::state::open;
1428 
1429  if (m_open_handler) {
1431  }
1432 
1434 }
1435 
1436 template <typename config>
1438  m_alog.write(log::alevel::devel,"connection send_http_request");
1439 
1440  // TODO: origin header?
1441 
1442  // Have the protocol processor fill in the appropriate fields based on the
1443  // selected client version
1444  if (m_processor) {
1445  lib::error_code ec;
1448 
1449  if (ec) {
1450  log_err(log::elevel::fatal,"Internal library error: Processor",ec);
1451  return;
1452  }
1453  } else {
1454  m_elog.write(log::elevel::fatal,"Internal library error: missing processor");
1455  return;
1456  }
1457 
1458  // Unless the user has overridden the user agent, send generic WS++ UA.
1459  if (m_request.get_header("User-Agent").empty()) {
1460  if (!m_user_agent.empty()) {
1461  m_request.replace_header("User-Agent",m_user_agent);
1462  } else {
1463  m_request.remove_header("User-Agent");
1464  }
1465  }
1466 
1468 
1469  if (m_alog.static_test(log::alevel::devel)) {
1470  m_alog.write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
1471  }
1472 
1473  if (m_open_handshake_timeout_dur > 0) {
1476  lib::bind(
1478  type::get_shared(),
1479  lib::placeholders::_1
1480  )
1481  );
1482  }
1483 
1487  lib::bind(
1489  type::get_shared(),
1490  lib::placeholders::_1
1491  )
1492  );
1493 }
1494 
1495 template <typename config>
1497  m_alog.write(log::alevel::devel,"handle_send_http_request");
1498 
1499  lib::error_code ecm = ec;
1500 
1501  if (!ecm) {
1503 
1504  if (m_state == session::state::connecting) {
1507  } else {
1509  }
1510  } else if (m_state == session::state::closed) {
1511  // The connection was canceled while the response was being sent,
1512  // usually by the handshake timer. This is basically expected
1513  // (though hopefully rare) and there is nothing we can do so ignore.
1515  "handle_send_http_request invoked after connection was closed");
1516  return;
1517  } else {
1519  }
1520  }
1521 
1522  if (ecm) {
1523  if (ecm == transport::error::eof && m_state == session::state::closed) {
1524  // we expect to get eof if the connection is closed already
1526  "got (expected) eof/state error from closed con");
1527  return;
1528  }
1529 
1530  log_err(log::elevel::rerror,"handle_send_http_request",ecm);
1531  this->terminate(ecm);
1532  return;
1533  }
1534 
1536  1,
1537  m_buf,
1539  lib::bind(
1541  type::get_shared(),
1542  lib::placeholders::_1,
1543  lib::placeholders::_2
1544  )
1545  );
1546 }
1547 
1548 template <typename config>
1551 {
1552  m_alog.write(log::alevel::devel,"handle_read_http_response");
1553 
1554  lib::error_code ecm = ec;
1555 
1556  if (!ecm) {
1558 
1559  if (m_state == session::state::connecting) {
1562  }
1563  } else if (m_state == session::state::closed) {
1564  // The connection was canceled while the response was being sent,
1565  // usually by the handshake timer. This is basically expected
1566  // (though hopefully rare) and there is nothing we can do so ignore.
1568  "handle_read_http_response invoked after connection was closed");
1569  return;
1570  } else {
1572  }
1573  }
1574 
1575  if (ecm) {
1576  if (ecm == transport::error::eof && m_state == session::state::closed) {
1577  // we expect to get eof if the connection is closed already
1579  "got (expected) eof/state error from closed con");
1580  return;
1581  }
1582 
1583  log_err(log::elevel::rerror,"handle_read_http_response",ecm);
1584  this->terminate(ecm);
1585  return;
1586  }
1587 
1588  size_t bytes_processed = 0;
1589  // TODO: refactor this to use error codes rather than exceptions
1590  try {
1592  } catch (http::exception & e) {
1594  std::string("error in handle_read_http_response: ")+e.what());
1596  return;
1597  }
1598 
1599  m_alog.write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
1600 
1601  if (m_response.headers_ready()) {
1602  if (m_handshake_timer) {
1605  }
1606 
1608  m_request,
1609  m_response
1610  );
1611  if (validate_ec) {
1612  log_err(log::elevel::rerror,"Server handshake response",validate_ec);
1613  this->terminate(validate_ec);
1614  return;
1615  }
1616 
1617  // Read extension parameters and set up values necessary for the end
1618  // user to complete extension negotiation.
1621 
1622  if (neg_results.first) {
1623  // There was a fatal error in extension negotiation. For the moment
1624  // kill all connections that fail extension negotiation.
1625 
1626  // TODO: deal with cases where the response is well formed but
1627  // doesn't match the options requested by the client. Its possible
1628  // that the best behavior in this cases is to log and continue with
1629  // an unextended connection.
1630  m_alog.write(log::alevel::devel, "Extension negotiation failed: "
1631  + neg_results.first.message());
1633  // TODO: close connection with reason 1010 (and list extensions)
1634  }
1635 
1636  // response is valid, connection can now be assumed to be open
1638  m_state = session::state::open;
1639 
1640  this->log_open_result();
1641 
1642  if (m_open_handler) {
1644  }
1645 
1646  // The remaining bytes in m_buf are frame data. Copy them to the
1647  // beginning of the buffer and note the length. They will be read after
1648  // the handshake completes and before more bytes are read.
1651 
1653  } else {
1655  1,
1656  m_buf,
1658  lib::bind(
1660  type::get_shared(),
1661  lib::placeholders::_1,
1662  lib::placeholders::_2
1663  )
1664  );
1665  }
1666 }
1667 
1668 template <typename config>
1670  lib::error_code const & ec)
1671 {
1672  if (ec == transport::error::operation_aborted) {
1673  m_alog.write(log::alevel::devel,"open handshake timer cancelled");
1674  } else if (ec) {
1676  "open handle_open_handshake_timeout error: "+ec.message());
1677  // TODO: ignore or fail here?
1678  } else {
1679  m_alog.write(log::alevel::devel,"open handshake timer expired");
1681  }
1682 }
1683 
1684 template <typename config>
1686  lib::error_code const & ec)
1687 {
1688  if (ec == transport::error::operation_aborted) {
1689  m_alog.write(log::alevel::devel,"asio close handshake timer cancelled");
1690  } else if (ec) {
1692  "asio open handle_close_handshake_timeout error: "+ec.message());
1693  // TODO: ignore or fail here?
1694  } else {
1695  m_alog.write(log::alevel::devel, "asio close handshake timer expired");
1697  }
1698 }
1699 
1700 template <typename config>
1701 void connection<config>::terminate(lib::error_code const & ec) {
1702  if (m_alog.static_test(log::alevel::devel)) {
1703  m_alog.write(log::alevel::devel,"connection terminate");
1704  }
1705 
1706  // Cancel close handshake timer
1707  if (m_handshake_timer) {
1710  }
1711 
1713  if (ec) {
1714  m_ec = ec;
1717  }
1718 
1719  // TODO: does any of this need a mutex?
1720  if (m_is_http) {
1722  }
1723  if (m_state == session::state::connecting) {
1725  tstat = failed;
1726 
1727  // Log fail result here before socket is shut down and we can't get
1728  // the remote address, etc anymore
1729  if (m_ec != error::http_connection_ended) {
1730  log_fail_result();
1731  }
1732  } else if (m_state != session::state::closed) {
1734  tstat = closed;
1735  } else {
1737  "terminate called on connection that was already terminated");
1738  return;
1739  }
1740 
1741  // TODO: choose between shutdown and close based on error code sent
1742 
1744  lib::bind(
1746  type::get_shared(),
1747  tstat,
1748  lib::placeholders::_1
1749  )
1750  );
1751 }
1752 
1753 template <typename config>
1755  lib::error_code const & ec)
1756 {
1757  if (m_alog.static_test(log::alevel::devel)) {
1758  m_alog.write(log::alevel::devel,"connection handle_terminate");
1759  }
1760 
1761  if (ec) {
1762  // there was an error actually shutting down the connection
1763  log_err(log::elevel::devel,"handle_terminate",ec);
1764  }
1765 
1766  // clean shutdown
1767  if (tstat == failed) {
1768  if (m_ec != error::http_connection_ended) {
1769  if (m_fail_handler) {
1771  }
1772  }
1773  } else if (tstat == closed) {
1774  if (m_close_handler) {
1776  }
1777  log_close_result();
1778  } else {
1779  m_elog.write(log::elevel::rerror,"Unknown terminate_status");
1780  }
1781 
1782  // call the termination handler if it exists
1783  // if it exists it might (but shouldn't) refer to a bad memory location.
1784  // If it does, we don't care and should catch and ignore it.
1785  if (m_termination_handler) {
1786  try {
1788  } catch (std::exception const & e) {
1790  std::string("termination_handler call failed. Reason was: ")+e.what());
1791  }
1792  }
1793 }
1794 
1795 template <typename config>
1797  //m_alog.write(log::alevel::devel,"connection write_frame");
1798 
1799  {
1801 
1802  // Check the write flag. If true, there is an outstanding transport
1803  // write already. In this case we just return. The write handler will
1804  // start a new write if the write queue isn't empty. If false, we set
1805  // the write flag and proceed to initiate a transport write.
1806  if (m_write_flag) {
1807  return;
1808  }
1809 
1810  // pull off all the messages that are ready to write.
1811  // stop if we get a message marked terminal
1813  while (next_message) {
1815  if (!next_message->get_terminal()) {
1816  next_message = write_pop();
1817  } else {
1819  }
1820  }
1821 
1822  if (m_current_msgs.empty()) {
1823  // there was nothing to send
1824  return;
1825  } else {
1826  // At this point we own the next messages to be sent and are
1827  // responsible for holding the write flag until they are
1828  // successfully sent or there is some error
1829  m_write_flag = true;
1830  }
1831  }
1832 
1833  typename std::vector<message_ptr>::iterator it;
1834  for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1835  std::string const & header = (*it)->get_header();
1836  std::string const & payload = (*it)->get_payload();
1837 
1840  }
1841 
1842  // Print detailed send stats if those log levels are enabled
1846 
1847  general << "Dispatching write containing " << m_current_msgs.size()
1848  <<" message(s) containing ";
1849  header << "Header Bytes: \n";
1850  payload << "Payload Bytes: \n";
1851 
1852  size_t hbytes = 0;
1853  size_t pbytes = 0;
1854 
1855  for (size_t i = 0; i < m_current_msgs.size(); i++) {
1858 
1859 
1860  header << "[" << i << "] ("
1861  << m_current_msgs[i]->get_header().size() << ") "
1862  << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
1863 
1866  payload << "[" << i << "] ("
1867  << m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
1868  << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1871  )
1872  << "\n";
1873  }
1874  }
1875  }
1876 
1877  general << hbytes << " header bytes and " << pbytes << " payload bytes";
1878 
1882  }
1883  }
1884 
1886  m_send_buffer,
1888  );
1889 }
1890 
1891 template <typename config>
1893 {
1894  if (m_alog.static_test(log::alevel::devel)) {
1895  m_alog.write(log::alevel::devel,"connection handle_write_frame");
1896  }
1897 
1899 
1900  m_send_buffer.clear();
1902  // TODO: recycle instead of deleting
1903 
1904  if (ec) {
1905  log_err(log::elevel::fatal,"handle_write_frame",ec);
1906  this->terminate(ec);
1907  return;
1908  }
1909 
1910  if (terminal) {
1911  this->terminate(lib::error_code());
1912  return;
1913  }
1914 
1915  bool needs_writing = false;
1916  {
1918 
1919  // release write flag
1920  m_write_flag = false;
1921 
1923  }
1924 
1925  if (needs_writing) {
1927  &type::write_frame,
1928  type::get_shared()
1929  ));
1930  }
1931 }
1932 
1933 template <typename config>
1935 {
1936  return versions_supported;
1937 }
1938 
1939 template <typename config>
1941 {
1942  m_alog.write(log::alevel::devel,"process_control_frame");
1943 
1944  frame::opcode::value op = msg->get_opcode();
1945  lib::error_code ec;
1946 
1947  std::stringstream s;
1948  s << "Control frame received with opcode " << op;
1950 
1951  if (m_state == session::state::closed) {
1952  m_elog.write(log::elevel::warn,"got frame in state closed");
1953  return;
1954  }
1955  if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1956  m_elog.write(log::elevel::warn,"got non-close frame in state closing");
1957  return;
1958  }
1959 
1960  if (op == frame::opcode::PING) {
1961  bool should_reply = true;
1962 
1963  if (m_ping_handler) {
1965  }
1966 
1967  if (should_reply) {
1968  this->pong(msg->get_payload(),ec);
1969  if (ec) {
1970  log_err(log::elevel::devel,"Failed to send response pong",ec);
1971  }
1972  }
1973  } else if (op == frame::opcode::PONG) {
1974  if (m_pong_handler) {
1976  }
1977  if (m_ping_timer) {
1978  m_ping_timer->cancel();
1979  }
1980  } else if (op == frame::opcode::CLOSE) {
1981  m_alog.write(log::alevel::devel,"got close frame");
1982  // record close code and reason somewhere
1983 
1985  if (ec) {
1986  s.str("");
1988  s << "Received invalid close code " << m_remote_close_code
1989  << " dropping connection per config.";
1990  m_elog.write(log::elevel::devel,s.str());
1991  this->terminate(ec);
1992  } else {
1993  s << "Received invalid close code " << m_remote_close_code
1994  << " sending acknowledgement and closing";
1995  m_elog.write(log::elevel::devel,s.str());
1997  "Invalid close code");
1998  if (ec) {
1999  log_err(log::elevel::devel,"send_close_ack",ec);
2000  }
2001  }
2002  return;
2003  }
2004 
2006  if (ec) {
2009  "Received invalid close reason. Dropping connection per config");
2010  this->terminate(ec);
2011  } else {
2013  "Received invalid close reason. Sending acknowledgement and closing");
2015  "Invalid close reason");
2016  if (ec) {
2017  log_err(log::elevel::devel,"send_close_ack",ec);
2018  }
2019  }
2020  return;
2021  }
2022 
2023  if (m_state == session::state::open) {
2024  s.str("");
2025  s << "Received close frame with code " << m_remote_close_code
2026  << " and reason " << m_remote_close_reason;
2027  m_alog.write(log::alevel::devel,s.str());
2028 
2029  ec = send_close_ack();
2030  if (ec) {
2031  log_err(log::elevel::devel,"send_close_ack",ec);
2032  }
2033  } else if (m_state == session::state::closing && !m_was_clean) {
2034  // ack of our close
2035  m_alog.write(log::alevel::devel, "Got acknowledgement of close");
2036 
2037  m_was_clean = true;
2038 
2039  // If we are a server terminate the connection now. Clients should
2040  // leave the connection open to give the server an opportunity to
2041  // initiate the TCP close. The client's timer will handle closing
2042  // its side of the connection if the server misbehaves.
2043  //
2044  // TODO: different behavior if the underlying transport doesn't
2045  // support timers?
2046  if (m_is_server) {
2047  terminate(lib::error_code());
2048  }
2049  } else {
2050  // spurious, ignore
2051  m_elog.write(log::elevel::devel, "Got close frame in wrong state");
2052  }
2053  } else {
2054  // got an invalid control opcode
2055  m_elog.write(log::elevel::devel, "Got control frame with invalid opcode");
2056  // initiate protocol error shutdown
2057  }
2058 }
2059 
2060 template <typename config>
2062  std::string const & reason)
2063 {
2064  return send_close_frame(code,reason,true,m_is_server);
2065 }
2066 
2067 template <typename config>
2069  std::string const & reason, bool ack, bool terminal)
2070 {
2071  m_alog.write(log::alevel::devel,"send_close_frame");
2072 
2073  // check for special codes
2074 
2075  // If silent close is set, respect it and blank out close information
2076  // Otherwise use whatever has been specified in the parameters. If
2077  // parameters specifies close::status::blank then determine what to do
2078  // based on whether or not this is an ack. If it is not an ack just
2079  // send blank info. If it is an ack then echo the close information from
2080  // the remote endpoint.
2081  if (config::silent_close) {
2082  m_alog.write(log::alevel::devel,"closing silently");
2085  } else if (code != close::status::blank) {
2086  m_alog.write(log::alevel::devel,"closing with specified codes");
2089  } else if (!ack) {
2090  m_alog.write(log::alevel::devel,"closing with no status code");
2093  } else if (m_remote_close_code == close::status::no_status) {
2095  "acknowledging a no-status close with normal code");
2098  } else {
2099  m_alog.write(log::alevel::devel,"acknowledging with remote codes");
2102  }
2103 
2104  std::stringstream s;
2105  s << "Closing with code: " << m_local_close_code << ", and reason: "
2107  m_alog.write(log::alevel::devel,s.str());
2108 
2110  if (!msg) {
2112  }
2113 
2116  if (ec) {
2117  return ec;
2118  }
2119 
2120  // Messages flagged terminal will result in the TCP connection being dropped
2121  // after the message has been written. This is typically used when servers
2122  // send an ack and when any endpoint encounters a protocol error
2123  if (terminal) {
2124  msg->set_terminal(true);
2125  }
2126 
2128 
2129  if (ack) {
2130  m_was_clean = true;
2131  }
2132 
2133  // Start a timer so we don't wait forever for the acknowledgement close
2134  // frame
2138  lib::bind(
2140  type::get_shared(),
2141  lib::placeholders::_1
2142  )
2143  );
2144  }
2145 
2146  bool needs_writing = false;
2147  {
2149  write_push(msg);
2151  }
2152 
2153  if (needs_writing) {
2155  &type::write_frame,
2156  type::get_shared()
2157  ));
2158  }
2159 
2160  return lib::error_code();
2161 }
2162 
2163 template <typename config>
2164 typename connection<config>::processor_ptr
2165 connection<config>::get_processor(int version) const {
2166  // TODO: allow disabling certain versions
2167 
2168  processor_ptr p;
2169 
2170  switch (version) {
2171  case 0:
2174  m_is_server,
2176  );
2177  break;
2178  case 7:
2181  m_is_server,
2182  m_msg_manager,
2183  lib::ref(m_rng)
2184  );
2185  break;
2186  case 8:
2189  m_is_server,
2190  m_msg_manager,
2191  lib::ref(m_rng)
2192  );
2193  break;
2194  case 13:
2197  m_is_server,
2198  m_msg_manager,
2199  lib::ref(m_rng)
2200  );
2201  break;
2202  default:
2203  return p;
2204  }
2205 
2206  // Settings not configured by the constructor
2208 
2209  return p;
2210 }
2211 
2212 template <typename config>
2214 {
2215  if (!msg) {
2216  return;
2217  }
2218 
2221 
2222  if (m_alog.static_test(log::alevel::devel)) {
2223  std::stringstream s;
2224  s << "write_push: message count: " << m_send_queue.size()
2225  << " buffer size: " << m_send_buffer_size;
2226  m_alog.write(log::alevel::devel,s.str());
2227  }
2228 }
2229 
2230 template <typename config>
2232 {
2233  message_ptr msg;
2234 
2235  if (m_send_queue.empty()) {
2236  return msg;
2237  }
2238 
2239  msg = m_send_queue.front();
2240 
2242  m_send_queue.pop();
2243 
2244  if (m_alog.static_test(log::alevel::devel)) {
2245  std::stringstream s;
2246  s << "write_pop: message count: " << m_send_queue.size()
2247  << " buffer size: " << m_send_buffer_size;
2248  m_alog.write(log::alevel::devel,s.str());
2249  }
2250  return msg;
2251 }
2252 
2253 template <typename config>
2255 {
2256  std::stringstream s;
2257 
2258  int version;
2260  version = -1;
2261  } else {
2263  }
2264 
2265  // Connection Type
2266  s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
2267 
2268  // Remote endpoint address
2270 
2271  // Version string if WebSocket
2272  if (version != -1) {
2273  s << "v" << version << " ";
2274  }
2275 
2276  // User Agent
2277  std::string ua = m_request.get_header("User-Agent");
2278  if (ua.empty()) {
2279  s << "\"\" ";
2280  } else {
2281  // check if there are any quotes in the user agent
2282  s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2283  }
2284 
2285  // URI
2286  s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
2287 
2288  // Status code
2290 
2292 }
2293 
2294 template <typename config>
2296 {
2297  std::stringstream s;
2298 
2299  s << "Disconnect "
2300  << "close local:[" << m_local_close_code
2302  << "] remote:[" << m_remote_close_code
2303  << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]";
2304 
2306 }
2307 
2308 template <typename config>
2310 {
2311  std::stringstream s;
2312 
2314 
2315  // Connection Type
2316  s << "WebSocket Connection ";
2317 
2318  // Remote endpoint address & WebSocket version
2320  if (version < 0) {
2321  s << " -";
2322  } else {
2323  s << " v" << version;
2324  }
2325 
2326  // User Agent
2327  std::string ua = m_request.get_header("User-Agent");
2328  if (ua.empty()) {
2329  s << " \"\" ";
2330  } else {
2331  // check if there are any quotes in the user agent
2332  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2333  }
2334 
2335  // URI
2336  s << (m_uri ? m_uri->get_resource() : "-");
2337 
2338  // HTTP Status code
2339  s << " " << m_response.get_status_code();
2340 
2341  // WebSocket++ error code & reason
2342  s << " " << m_ec << " " << m_ec.message();
2343 
2344  m_alog.write(log::alevel::fail,s.str());
2345 }
2346 
2347 template <typename config>
2349  std::stringstream s;
2350 
2352  m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket");
2353  return;
2354  }
2355 
2356  // Connection Type
2357  s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host"))
2359  << " \"" << m_request.get_method()
2360  << " " << (m_uri ? m_uri->get_resource() : "-")
2361  << " " << m_request.get_version() << "\" " << m_response.get_status_code()
2362  << " " << m_response.get_body().size();
2363 
2364  // User Agent
2365  std::string ua = m_request.get_header("User-Agent");
2366  if (ua.empty()) {
2367  s << " \"\" ";
2368  } else {
2369  // check if there are any quotes in the user agent
2370  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2371  }
2372 
2373  m_alog.write(log::alevel::http,s.str());
2374 }
2375 
2376 } // namespace websocketpp
2377 
2378 #endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
void handle_accept(connection_ptr con, lib::error_code const &ec)
Handler callback for start_accept.