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