WebSocket++  0.8.0-dev
C++ websocket client/server library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
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 }
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();
754  m_elog->write(log::elevel::rerror,s.str());
755 
756  this->terminate(ecm);
757  return;
758  }
759 
760  // At this point the transport is ready to read and write bytes.
761  if (m_is_server) {
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";
873  m_alog->write(log::alevel::devel,s.str());
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 
1225  if (neg_results.first) {
1226  // There was a fatal error in extension parsing that should result in
1227  // a failed connection attempt.
1228  m_alog->write(log::alevel::devel, "Bad request: " + neg_results.first.message());
1230  return neg_results.first;
1231  } else {
1232  // extension negotiation succeeded, set response header accordingly
1233  // we don't send an empty extensions header because it breaks many
1234  // clients.
1235  if (neg_results.second.size() > 0) {
1236  m_response.replace_header("Sec-WebSocket-Extensions",
1238  }
1239  }
1240 
1241  // extract URI from request
1243 
1244 
1245  if (!m_uri->get_valid()) {
1246  m_alog->write(log::alevel::devel, "Bad request: failed to parse uri");
1249  }
1250 
1251  // extract subprotocols
1254 
1255  if (subp_ec) {
1256  // should we do anything?
1257  }
1258 
1259  // Ask application to validate the connection
1262 
1263  // Write the appropriate response headers based on request and
1264  // processor version
1266 
1267  if (ec) {
1268  std::stringstream s;
1269  s << "Processing error: " << ec << "(" << ec.message() << ")";
1270  m_alog->write(log::alevel::devel, s.str());
1271 
1273  return ec;
1274  }
1275  } else {
1276  // User application has rejected the handshake
1277  m_alog->write(log::alevel::devel, "USER REJECT");
1278 
1279  // Use Bad Request if the user handler did not provide a more
1280  // specific http response error code.
1281  // TODO: is there a better default?
1284  }
1285 
1286  return error::make_error_code(error::rejected);
1287  }
1288 
1289  return lib::error_code();
1290 }
1291 
1292 template <typename config>
1294  m_alog->write(log::alevel::devel,"connection write_http_response");
1295 
1297  m_alog->write(log::alevel::http,"An HTTP handler took over the connection.");
1298  return;
1299  }
1300 
1304  } else {
1305  m_ec = ec;
1306  }
1307 
1308  m_response.set_version("HTTP/1.1");
1309 
1310  // Set server header based on the user agent settings
1311  if (m_response.get_header("Server").empty()) {
1312  if (!m_user_agent.empty()) {
1314  } else {
1315  m_response.remove_header("Server");
1316  }
1317  }
1318 
1319  // have the processor generate the raw bytes for the wire (if it exists)
1320  if (m_processor) {
1322  } else {
1323  // a processor wont exist for raw HTTP responses.
1325  }
1326 
1327  if (m_alog->static_test(log::alevel::devel)) {
1328  m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
1329  if (!m_response.get_header("Sec-WebSocket-Key3").empty()) {
1331  utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
1332  }
1333  }
1334 
1335  // write raw bytes
1339  lib::bind(
1341  type::get_shared(),
1342  lib::placeholders::_1
1343  )
1344  );
1345 }
1346 
1347 template <typename config>
1349  m_alog->write(log::alevel::devel,"handle_write_http_response");
1350 
1351  lib::error_code ecm = ec;
1352 
1353  if (!ecm) {
1355 
1356  if (m_state == session::state::connecting) {
1359  }
1360  } else if (m_state == session::state::closed) {
1361  // The connection was canceled while the response was being sent,
1362  // usually by the handshake timer. This is basically expected
1363  // (though hopefully rare) and there is nothing we can do so ignore.
1365  "handle_write_http_response invoked after connection was closed");
1366  return;
1367  } else {
1369  }
1370  }
1371 
1372  if (ecm) {
1373  if (ecm == transport::error::eof && m_state == session::state::closed) {
1374  // we expect to get eof if the connection is closed already
1376  "got (expected) eof/state error from closed con");
1377  return;
1378  }
1379 
1380  log_err(log::elevel::rerror,"handle_write_http_response",ecm);
1381  this->terminate(ecm);
1382  return;
1383  }
1384 
1385  if (m_handshake_timer) {
1388  }
1389 
1391  {
1392  /*if (m_processor || m_ec == error::http_parse_error ||
1393  m_ec == error::invalid_version || m_ec == error::unsupported_version
1394  || m_ec == error::upgrade_required)
1395  {*/
1396  if (!m_is_http) {
1397  std::stringstream s;
1398  s << "Handshake ended with HTTP error: "
1400  m_elog->write(log::elevel::rerror,s.str());
1401  } else {
1402  // if this was not a websocket connection, we have written
1403  // the expected response and the connection can be closed.
1404 
1405  this->log_http_result();
1406 
1407  if (m_ec) {
1409  "got to writing HTTP results with m_ec set: "+m_ec.message());
1410  }
1412  }
1413 
1414  this->terminate(m_ec);
1415  return;
1416  }
1417 
1418  this->log_open_result();
1419 
1421  m_state = session::state::open;
1422 
1423  if (m_open_handler) {
1425  }
1426 
1428 }
1429 
1430 template <typename config>
1432  m_alog->write(log::alevel::devel,"connection send_http_request");
1433 
1434  // TODO: origin header?
1435 
1436  // Have the protocol processor fill in the appropriate fields based on the
1437  // selected client version
1438  if (m_processor) {
1439  lib::error_code ec;
1442 
1443  if (ec) {
1444  log_err(log::elevel::fatal,"Internal library error: Processor",ec);
1445  return;
1446  }
1447  } else {
1448  m_elog->write(log::elevel::fatal,"Internal library error: missing processor");
1449  return;
1450  }
1451 
1452  // Unless the user has overridden the user agent, send generic WS++ UA.
1453  if (m_request.get_header("User-Agent").empty()) {
1454  if (!m_user_agent.empty()) {
1455  m_request.replace_header("User-Agent",m_user_agent);
1456  } else {
1457  m_request.remove_header("User-Agent");
1458  }
1459  }
1460 
1462 
1463  if (m_alog->static_test(log::alevel::devel)) {
1464  m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
1465  }
1466 
1467  if (m_open_handshake_timeout_dur > 0) {
1470  lib::bind(
1472  type::get_shared(),
1473  lib::placeholders::_1
1474  )
1475  );
1476  }
1477 
1481  lib::bind(
1483  type::get_shared(),
1484  lib::placeholders::_1
1485  )
1486  );
1487 }
1488 
1489 template <typename config>
1491  m_alog->write(log::alevel::devel,"handle_send_http_request");
1492 
1493  lib::error_code ecm = ec;
1494 
1495  if (!ecm) {
1497 
1498  if (m_state == session::state::connecting) {
1501  } else {
1503  }
1504  } else if (m_state == session::state::closed) {
1505  // The connection was canceled while the response was being sent,
1506  // usually by the handshake timer. This is basically expected
1507  // (though hopefully rare) and there is nothing we can do so ignore.
1509  "handle_send_http_request invoked after connection was closed");
1510  return;
1511  } else {
1513  }
1514  }
1515 
1516  if (ecm) {
1517  if (ecm == transport::error::eof && m_state == session::state::closed) {
1518  // we expect to get eof if the connection is closed already
1520  "got (expected) eof/state error from closed con");
1521  return;
1522  }
1523 
1524  log_err(log::elevel::rerror,"handle_send_http_request",ecm);
1525  this->terminate(ecm);
1526  return;
1527  }
1528 
1530  1,
1531  m_buf,
1533  lib::bind(
1535  type::get_shared(),
1536  lib::placeholders::_1,
1537  lib::placeholders::_2
1538  )
1539  );
1540 }
1541 
1542 template <typename config>
1545 {
1546  m_alog->write(log::alevel::devel,"handle_read_http_response");
1547 
1548  lib::error_code ecm = ec;
1549 
1550  if (!ecm) {
1552 
1553  if (m_state == session::state::connecting) {
1556  }
1557  } else if (m_state == session::state::closed) {
1558  // The connection was canceled while the response was being sent,
1559  // usually by the handshake timer. This is basically expected
1560  // (though hopefully rare) and there is nothing we can do so ignore.
1562  "handle_read_http_response invoked after connection was closed");
1563  return;
1564  } else {
1566  }
1567  }
1568 
1569  if (ecm) {
1570  if (ecm == transport::error::eof && m_state == session::state::closed) {
1571  // we expect to get eof if the connection is closed already
1573  "got (expected) eof/state error from closed con");
1574  return;
1575  }
1576 
1577  log_err(log::elevel::rerror,"handle_read_http_response",ecm);
1578  this->terminate(ecm);
1579  return;
1580  }
1581 
1582  size_t bytes_processed = 0;
1583  // TODO: refactor this to use error codes rather than exceptions
1584  try {
1586  } catch (http::exception & e) {
1588  std::string("error in handle_read_http_response: ")+e.what());
1590  return;
1591  }
1592 
1593  m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
1594 
1595  if (m_response.headers_ready()) {
1596  if (m_handshake_timer) {
1599  }
1600 
1602  m_request,
1603  m_response
1604  );
1605  if (validate_ec) {
1606  log_err(log::elevel::rerror,"Server handshake response",validate_ec);
1607  this->terminate(validate_ec);
1608  return;
1609  }
1610 
1611  // Read extension parameters and set up values necessary for the end
1612  // user to complete extension negotiation.
1615 
1616  if (neg_results.first) {
1617  // There was a fatal error in extension negotiation. For the moment
1618  // kill all connections that fail extension negotiation.
1619 
1620  // TODO: deal with cases where the response is well formed but
1621  // doesn't match the options requested by the client. Its possible
1622  // that the best behavior in this cases is to log and continue with
1623  // an unextended connection.
1624  m_alog->write(log::alevel::devel, "Extension negotiation failed: "
1625  + neg_results.first.message());
1627  // TODO: close connection with reason 1010 (and list extensions)
1628  }
1629 
1630  // response is valid, connection can now be assumed to be open
1632  m_state = session::state::open;
1633 
1634  this->log_open_result();
1635 
1636  if (m_open_handler) {
1638  }
1639 
1640  // The remaining bytes in m_buf are frame data. Copy them to the
1641  // beginning of the buffer and note the length. They will be read after
1642  // the handshake completes and before more bytes are read.
1645 
1647  } else {
1649  1,
1650  m_buf,
1652  lib::bind(
1654  type::get_shared(),
1655  lib::placeholders::_1,
1656  lib::placeholders::_2
1657  )
1658  );
1659  }
1660 }
1661 
1662 template <typename config>
1664  lib::error_code const & ec)
1665 {
1666  if (ec == transport::error::operation_aborted) {
1667  m_alog->write(log::alevel::devel,"open handshake timer cancelled");
1668  } else if (ec) {
1670  "open handle_open_handshake_timeout error: "+ec.message());
1671  // TODO: ignore or fail here?
1672  } else {
1673  m_alog->write(log::alevel::devel,"open handshake timer expired");
1675  }
1676 }
1677 
1678 template <typename config>
1680  lib::error_code const & ec)
1681 {
1682  if (ec == transport::error::operation_aborted) {
1683  m_alog->write(log::alevel::devel,"asio close handshake timer cancelled");
1684  } else if (ec) {
1686  "asio open handle_close_handshake_timeout error: "+ec.message());
1687  // TODO: ignore or fail here?
1688  } else {
1689  m_alog->write(log::alevel::devel, "asio close handshake timer expired");
1691  }
1692 }
1693 
1694 template <typename config>
1695 void connection<config>::terminate(lib::error_code const & ec) {
1696  if (m_alog->static_test(log::alevel::devel)) {
1697  m_alog->write(log::alevel::devel,"connection terminate");
1698  }
1699 
1700  // Cancel close handshake timer
1701  if (m_handshake_timer) {
1704  }
1705 
1707  if (ec) {
1708  m_ec = ec;
1711  }
1712 
1713  // TODO: does any of this need a mutex?
1714  if (m_is_http) {
1716  }
1717  if (m_state == session::state::connecting) {
1719  tstat = failed;
1720 
1721  // Log fail result here before socket is shut down and we can't get
1722  // the remote address, etc anymore
1723  if (m_ec != error::http_connection_ended) {
1724  log_fail_result();
1725  }
1726  } else if (m_state != session::state::closed) {
1728  tstat = closed;
1729  } else {
1731  "terminate called on connection that was already terminated");
1732  return;
1733  }
1734 
1735  // TODO: choose between shutdown and close based on error code sent
1736 
1738  lib::bind(
1740  type::get_shared(),
1741  tstat,
1742  lib::placeholders::_1
1743  )
1744  );
1745 }
1746 
1747 template <typename config>
1749  lib::error_code const & ec)
1750 {
1751  if (m_alog->static_test(log::alevel::devel)) {
1752  m_alog->write(log::alevel::devel,"connection handle_terminate");
1753  }
1754 
1755  if (ec) {
1756  // there was an error actually shutting down the connection
1757  log_err(log::elevel::devel,"handle_terminate",ec);
1758  }
1759 
1760  // clean shutdown
1761  if (tstat == failed) {
1762  if (m_ec != error::http_connection_ended) {
1763  if (m_fail_handler) {
1765  }
1766  }
1767  } else if (tstat == closed) {
1768  if (m_close_handler) {
1770  }
1771  log_close_result();
1772  } else {
1773  m_elog->write(log::elevel::rerror,"Unknown terminate_status");
1774  }
1775 
1776  // call the termination handler if it exists
1777  // if it exists it might (but shouldn't) refer to a bad memory location.
1778  // If it does, we don't care and should catch and ignore it.
1779  if (m_termination_handler) {
1780  try {
1782  } catch (std::exception const & e) {
1783  m_elog->write(log::elevel::warn,
1784  std::string("termination_handler call failed. Reason was: ")+e.what());
1785  }
1786  }
1787 }
1788 
1789 template <typename config>
1791  //m_alog->write(log::alevel::devel,"connection write_frame");
1792 
1793  {
1795 
1796  // Check the write flag. If true, there is an outstanding transport
1797  // write already. In this case we just return. The write handler will
1798  // start a new write if the write queue isn't empty. If false, we set
1799  // the write flag and proceed to initiate a transport write.
1800  if (m_write_flag) {
1801  return;
1802  }
1803 
1804  // pull off all the messages that are ready to write.
1805  // stop if we get a message marked terminal
1807  while (next_message) {
1809  if (!next_message->get_terminal()) {
1810  next_message = write_pop();
1811  } else {
1813  }
1814  }
1815 
1816  if (m_current_msgs.empty()) {
1817  // there was nothing to send
1818  return;
1819  } else {
1820  // At this point we own the next messages to be sent and are
1821  // responsible for holding the write flag until they are
1822  // successfully sent or there is some error
1823  m_write_flag = true;
1824  }
1825  }
1826 
1827  typename std::vector<message_ptr>::iterator it;
1828  for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1829  std::string const & header = (*it)->get_header();
1830  std::string const & payload = (*it)->get_payload();
1831 
1834  }
1835 
1836  // Print detailed send stats if those log levels are enabled
1840 
1841  general << "Dispatching write containing " << m_current_msgs.size()
1842  <<" message(s) containing ";
1843  header << "Header Bytes: \n";
1844  payload << "Payload Bytes: \n";
1845 
1846  size_t hbytes = 0;
1847  size_t pbytes = 0;
1848 
1849  for (size_t i = 0; i < m_current_msgs.size(); i++) {
1852 
1853 
1854  header << "[" << i << "] ("
1855  << m_current_msgs[i]->get_header().size() << ") "
1856  << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
1857 
1860  payload << "[" << i << "] ("
1861  << m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
1862  << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1865  )
1866  << "\n";
1867  }
1868  }
1869  }
1870 
1871  general << hbytes << " header bytes and " << pbytes << " payload bytes";
1872 
1876  }
1877  }
1878 
1880  m_send_buffer,
1882  );
1883 }
1884 
1885 template <typename config>
1887 {
1888  if (m_alog->static_test(log::alevel::devel)) {
1889  m_alog->write(log::alevel::devel,"connection handle_write_frame");
1890  }
1891 
1893 
1894  m_send_buffer.clear();
1896  // TODO: recycle instead of deleting
1897 
1898  if (ec) {
1899  log_err(log::elevel::fatal,"handle_write_frame",ec);
1900  this->terminate(ec);
1901  return;
1902  }
1903 
1904  if (terminal) {
1905  this->terminate(lib::error_code());
1906  return;
1907  }
1908 
1909  bool needs_writing = false;
1910  {
1912 
1913  // release write flag
1914  m_write_flag = false;
1915 
1917  }
1918 
1919  if (needs_writing) {
1921  &type::write_frame,
1922  type::get_shared()
1923  ));
1924  }
1925 }
1926 
1927 template <typename config>
1929 {
1930  return versions_supported;
1931 }
1932 
1933 template <typename config>
1935 {
1936  m_alog->write(log::alevel::devel,"process_control_frame");
1937 
1938  frame::opcode::value op = msg->get_opcode();
1939  lib::error_code ec;
1940 
1941  std::stringstream s;
1942  s << "Control frame received with opcode " << op;
1943  m_alog->write(log::alevel::control,s.str());
1944 
1945  if (m_state == session::state::closed) {
1946  m_elog->write(log::elevel::warn,"got frame in state closed");
1947  return;
1948  }
1949  if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1950  m_elog->write(log::elevel::warn,"got non-close frame in state closing");
1951  return;
1952  }
1953 
1954  if (op == frame::opcode::PING) {
1955  bool should_reply = true;
1956 
1957  if (m_ping_handler) {
1959  }
1960 
1961  if (should_reply) {
1962  this->pong(msg->get_payload(),ec);
1963  if (ec) {
1964  log_err(log::elevel::devel,"Failed to send response pong",ec);
1965  }
1966  }
1967  } else if (op == frame::opcode::PONG) {
1968  if (m_pong_handler) {
1970  }
1971  if (m_ping_timer) {
1972  m_ping_timer->cancel();
1973  }
1974  } else if (op == frame::opcode::CLOSE) {
1975  m_alog->write(log::alevel::devel,"got close frame");
1976  // record close code and reason somewhere
1977 
1979  if (ec) {
1980  s.str("");
1982  s << "Received invalid close code " << m_remote_close_code
1983  << " dropping connection per config.";
1984  m_elog->write(log::elevel::devel,s.str());
1985  this->terminate(ec);
1986  } else {
1987  s << "Received invalid close code " << m_remote_close_code
1988  << " sending acknowledgement and closing";
1989  m_elog->write(log::elevel::devel,s.str());
1991  "Invalid close code");
1992  if (ec) {
1993  log_err(log::elevel::devel,"send_close_ack",ec);
1994  }
1995  }
1996  return;
1997  }
1998 
2000  if (ec) {
2003  "Received invalid close reason. Dropping connection per config");
2004  this->terminate(ec);
2005  } else {
2007  "Received invalid close reason. Sending acknowledgement and closing");
2009  "Invalid close reason");
2010  if (ec) {
2011  log_err(log::elevel::devel,"send_close_ack",ec);
2012  }
2013  }
2014  return;
2015  }
2016 
2017  if (m_state == session::state::open) {
2018  s.str("");
2019  s << "Received close frame with code " << m_remote_close_code
2020  << " and reason " << m_remote_close_reason;
2021  m_alog->write(log::alevel::devel,s.str());
2022 
2023  ec = send_close_ack();
2024  if (ec) {
2025  log_err(log::elevel::devel,"send_close_ack",ec);
2026  }
2027  } else if (m_state == session::state::closing && !m_was_clean) {
2028  // ack of our close
2029  m_alog->write(log::alevel::devel, "Got acknowledgement of close");
2030 
2031  m_was_clean = true;
2032 
2033  // If we are a server terminate the connection now. Clients should
2034  // leave the connection open to give the server an opportunity to
2035  // initiate the TCP close. The client's timer will handle closing
2036  // its side of the connection if the server misbehaves.
2037  //
2038  // TODO: different behavior if the underlying transport doesn't
2039  // support timers?
2040  if (m_is_server) {
2041  terminate(lib::error_code());
2042  }
2043  } else {
2044  // spurious, ignore
2045  m_elog->write(log::elevel::devel, "Got close frame in wrong state");
2046  }
2047  } else {
2048  // got an invalid control opcode
2049  m_elog->write(log::elevel::devel, "Got control frame with invalid opcode");
2050  // initiate protocol error shutdown
2051  }
2052 }
2053 
2054 template <typename config>
2056  std::string const & reason)
2057 {
2058  return send_close_frame(code,reason,true,m_is_server);
2059 }
2060 
2061 template <typename config>
2063  std::string const & reason, bool ack, bool terminal)
2064 {
2065  m_alog->write(log::alevel::devel,"send_close_frame");
2066 
2067  // check for special codes
2068 
2069  // If silent close is set, respect it and blank out close information
2070  // Otherwise use whatever has been specified in the parameters. If
2071  // parameters specifies close::status::blank then determine what to do
2072  // based on whether or not this is an ack. If it is not an ack just
2073  // send blank info. If it is an ack then echo the close information from
2074  // the remote endpoint.
2075  if (config::silent_close) {
2076  m_alog->write(log::alevel::devel,"closing silently");
2079  } else if (code != close::status::blank) {
2080  m_alog->write(log::alevel::devel,"closing with specified codes");
2083  } else if (!ack) {
2084  m_alog->write(log::alevel::devel,"closing with no status code");
2087  } else if (m_remote_close_code == close::status::no_status) {
2089  "acknowledging a no-status close with normal code");
2092  } else {
2093  m_alog->write(log::alevel::devel,"acknowledging with remote codes");
2096  }
2097 
2098  std::stringstream s;
2099  s << "Closing with code: " << m_local_close_code << ", and reason: "
2101  m_alog->write(log::alevel::devel,s.str());
2102 
2104  if (!msg) {
2106  }
2107 
2110  if (ec) {
2111  return ec;
2112  }
2113 
2114  // Messages flagged terminal will result in the TCP connection being dropped
2115  // after the message has been written. This is typically used when servers
2116  // send an ack and when any endpoint encounters a protocol error
2117  if (terminal) {
2118  msg->set_terminal(true);
2119  }
2120 
2122 
2123  if (ack) {
2124  m_was_clean = true;
2125  }
2126 
2127  // Start a timer so we don't wait forever for the acknowledgement close
2128  // frame
2132  lib::bind(
2134  type::get_shared(),
2135  lib::placeholders::_1
2136  )
2137  );
2138  }
2139 
2140  bool needs_writing = false;
2141  {
2143  write_push(msg);
2145  }
2146 
2147  if (needs_writing) {
2149  &type::write_frame,
2150  type::get_shared()
2151  ));
2152  }
2153 
2154  return lib::error_code();
2155 }
2156 
2157 template <typename config>
2158 typename connection<config>::processor_ptr
2159 connection<config>::get_processor(int version) const {
2160  // TODO: allow disabling certain versions
2161 
2162  processor_ptr p;
2163 
2164  switch (version) {
2165  case 0:
2168  m_is_server,
2170  );
2171  break;
2172  case 7:
2175  m_is_server,
2176  m_msg_manager,
2177  lib::ref(m_rng)
2178  );
2179  break;
2180  case 8:
2183  m_is_server,
2184  m_msg_manager,
2185  lib::ref(m_rng)
2186  );
2187  break;
2188  case 13:
2191  m_is_server,
2192  m_msg_manager,
2193  lib::ref(m_rng)
2194  );
2195  break;
2196  default:
2197  return p;
2198  }
2199 
2200  // Settings not configured by the constructor
2202 
2203  return p;
2204 }
2205 
2206 template <typename config>
2208 {
2209  if (!msg) {
2210  return;
2211  }
2212 
2215 
2216  if (m_alog->static_test(log::alevel::devel)) {
2217  std::stringstream s;
2218  s << "write_push: message count: " << m_send_queue.size()
2219  << " buffer size: " << m_send_buffer_size;
2220  m_alog->write(log::alevel::devel,s.str());
2221  }
2222 }
2223 
2224 template <typename config>
2226 {
2227  message_ptr msg;
2228 
2229  if (m_send_queue.empty()) {
2230  return msg;
2231  }
2232 
2233  msg = m_send_queue.front();
2234 
2236  m_send_queue.pop();
2237 
2238  if (m_alog->static_test(log::alevel::devel)) {
2239  std::stringstream s;
2240  s << "write_pop: message count: " << m_send_queue.size()
2241  << " buffer size: " << m_send_buffer_size;
2242  m_alog->write(log::alevel::devel,s.str());
2243  }
2244  return msg;
2245 }
2246 
2247 template <typename config>
2249 {
2250  std::stringstream s;
2251 
2252  int version;
2254  version = -1;
2255  } else {
2257  }
2258 
2259  // Connection Type
2260  s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
2261 
2262  // Remote endpoint address
2264 
2265  // Version string if WebSocket
2266  if (version != -1) {
2267  s << "v" << version << " ";
2268  }
2269 
2270  // User Agent
2271  std::string ua = m_request.get_header("User-Agent");
2272  if (ua.empty()) {
2273  s << "\"\" ";
2274  } else {
2275  // check if there are any quotes in the user agent
2276  s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2277  }
2278 
2279  // URI
2280  s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
2281 
2282  // Status code
2284 
2285  m_alog->write(log::alevel::connect,s.str());
2286 }
2287 
2288 template <typename config>
2290 {
2291  std::stringstream s;
2292 
2293  s << "Disconnect "
2294  << "close local:[" << m_local_close_code
2296  << "] remote:[" << m_remote_close_code
2297  << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]";
2298 
2300 }
2301 
2302 template <typename config>
2304 {
2305  std::stringstream s;
2306 
2308 
2309  // Connection Type
2310  s << "WebSocket Connection ";
2311 
2312  // Remote endpoint address & WebSocket version
2314  if (version < 0) {
2315  s << " -";
2316  } else {
2317  s << " v" << version;
2318  }
2319 
2320  // User Agent
2321  std::string ua = m_request.get_header("User-Agent");
2322  if (ua.empty()) {
2323  s << " \"\" ";
2324  } else {
2325  // check if there are any quotes in the user agent
2326  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2327  }
2328 
2329  // URI
2330  s << (m_uri ? m_uri->get_resource() : "-");
2331 
2332  // HTTP Status code
2333  s << " " << m_response.get_status_code();
2334 
2335  // WebSocket++ error code & reason
2336  s << " " << m_ec << " " << m_ec.message();
2337 
2338  m_alog->write(log::alevel::fail,s.str());
2339 }
2340 
2341 template <typename config>
2343  std::stringstream s;
2344 
2346  m_alog->write(log::alevel::devel,"Call to log_http_result for WebSocket");
2347  return;
2348  }
2349 
2350  // Connection Type
2351  s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host"))
2353  << " \"" << m_request.get_method()
2354  << " " << (m_uri ? m_uri->get_resource() : "-")
2355  << " " << m_request.get_version() << "\" " << m_response.get_status_code()
2356  << " " << m_response.get_body().size();
2357 
2358  // User Agent
2359  std::string ua = m_request.get_header("User-Agent");
2360  if (ua.empty()) {
2361  s << " \"\" ";
2362  } else {
2363  // check if there are any quotes in the user agent
2364  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2365  }
2366 
2367  m_alog->write(log::alevel::http,s.str());
2368 }
2369 
2370 } // namespace websocketpp
2371 
2372 #endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
void handle_accept(connection_ptr con, lib::error_code const &ec)
Handler callback for start_accept.