websocketpp  0.6.0
C++/Boost Asio based websocket client/server library
connection_impl.hpp
1 /*
2  * Copyright (c) 2014, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the WebSocket++ Project nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP
29 #define WEBSOCKETPP_CONNECTION_IMPL_HPP
30 
31 #include <websocketpp/processors/hybi00.hpp>
32 #include <websocketpp/processors/hybi07.hpp>
33 #include <websocketpp/processors/hybi08.hpp>
34 #include <websocketpp/processors/hybi13.hpp>
35 
36 #include <websocketpp/processors/processor.hpp>
37 
38 #include <websocketpp/common/platforms.hpp>
39 #include <websocketpp/common/system_error.hpp>
40 
41 #include <algorithm>
42 #include <exception>
43 #include <sstream>
44 #include <string>
45 #include <utility>
46 #include <vector>
47 
48 namespace websocketpp {
49 
50 namespace istate = session::internal_state;
51 
52 template <typename config>
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 
639 
649 template <typename config>
651  // Cancel handshake timer, otherwise the connection will time out and we'll
652  // close the connection before the app has a chance to send a response.
653  if (m_handshake_timer) {
654  m_handshake_timer->cancel();
655  m_handshake_timer.reset();
656  }
657 
658  // Do something to signal deferral
659  m_http_state = session::http_state::deferred;
660 
661  return lib::error_code();
662 }
663 
665 
674 template <typename config>
675 void connection<config>::send_http_response(lib::error_code & ec) {
676  {
677  scoped_lock_type lock(m_connection_state_lock);
678  if (m_http_state != session::http_state::deferred) {
679  ec = error::make_error_code(error::invalid_state);
680  return;
681  }
682 
683  m_http_state = session::http_state::body_written;
684  }
685 
686  this->write_http_response(lib::error_code());
687  ec = lib::error_code();
688 }
689 
690 template <typename config>
692  lib::error_code ec;
693  this->send_http_response(ec);
694  if (ec) {
695  throw exception(ec);
696  }
697 }
698 
699 
700 
701 
702 /******** logic thread ********/
703 
704 template <typename config>
706  m_alog.write(log::alevel::devel,"connection start");
707 
708  if (m_internal_state != istate::USER_INIT) {
709  m_alog.write(log::alevel::devel,"Start called in invalid state");
710  this->terminate(error::make_error_code(error::invalid_state));
711  return;
712  }
713 
714  m_internal_state = istate::TRANSPORT_INIT;
715 
716  // Depending on how the transport implements init this function may return
717  // immediately and call handle_transport_init later or call
718  // handle_transport_init from this function.
719  transport_con_type::init(
720  lib::bind(
721  &type::handle_transport_init,
722  type::get_shared(),
723  lib::placeholders::_1
724  )
725  );
726 }
727 
728 template <typename config>
729 void connection<config>::handle_transport_init(lib::error_code const & ec) {
730  m_alog.write(log::alevel::devel,"connection handle_transport_init");
731 
732  lib::error_code ecm = ec;
733 
734  if (m_internal_state != istate::TRANSPORT_INIT) {
735  m_alog.write(log::alevel::devel,
736  "handle_transport_init must be called from transport init state");
737  ecm = error::make_error_code(error::invalid_state);
738  }
739 
740  if (ecm) {
741  std::stringstream s;
742  s << "handle_transport_init received error: "<< ecm.message();
743  m_elog.write(log::elevel::rerror,s.str());
744 
745  this->terminate(ecm);
746  return;
747  }
748 
749  // At this point the transport is ready to read and write bytes.
750  if (m_is_server) {
751  m_internal_state = istate::READ_HTTP_REQUEST;
752  this->read_handshake(1);
753  } else {
754  // We are a client. Set the processor to the version specified in the
755  // config file and send a handshake request.
756  m_internal_state = istate::WRITE_HTTP_REQUEST;
757  m_processor = get_processor(config::client_version);
758  this->send_http_request();
759  }
760 }
761 
762 template <typename config>
763 void connection<config>::read_handshake(size_t num_bytes) {
764  m_alog.write(log::alevel::devel,"connection read");
765 
766  if (m_open_handshake_timeout_dur > 0) {
767  m_handshake_timer = transport_con_type::set_timer(
768  m_open_handshake_timeout_dur,
769  lib::bind(
770  &type::handle_open_handshake_timeout,
771  type::get_shared(),
772  lib::placeholders::_1
773  )
774  );
775  }
776 
777  transport_con_type::async_read_at_least(
778  num_bytes,
779  m_buf,
780  config::connection_read_buffer_size,
781  lib::bind(
782  &type::handle_read_handshake,
783  type::get_shared(),
784  lib::placeholders::_1,
785  lib::placeholders::_2
786  )
787  );
788 }
789 
790 // All exit paths for this function need to call write_http_response() or submit
791 // a new read request with this function as the handler.
792 template <typename config>
793 void connection<config>::handle_read_handshake(lib::error_code const & ec,
794  size_t bytes_transferred)
795 {
796  m_alog.write(log::alevel::devel,"connection handle_read_handshake");
797 
798  lib::error_code ecm = ec;
799 
800  if (!ecm) {
801  scoped_lock_type lock(m_connection_state_lock);
802 
803  if (m_state == session::state::connecting) {
804  if (m_internal_state != istate::READ_HTTP_REQUEST) {
805  ecm = error::make_error_code(error::invalid_state);
806  }
807  } else if (m_state == session::state::closed) {
808  // The connection was canceled while the response was being sent,
809  // usually by the handshake timer. This is basically expected
810  // (though hopefully rare) and there is nothing we can do so ignore.
811  m_alog.write(log::alevel::devel,
812  "handle_read_handshake invoked after connection was closed");
813  return;
814  } else {
815  ecm = error::make_error_code(error::invalid_state);
816  }
817  }
818 
819  if (ecm) {
820  if (ecm == transport::error::eof && m_state == session::state::closed) {
821  // we expect to get eof if the connection is closed already
822  m_alog.write(log::alevel::devel,
823  "got (expected) eof/state error from closed con");
824  return;
825  }
826 
827  log_err(log::elevel::rerror,"handle_read_handshake",ecm);
828  this->terminate(ecm);
829  return;
830  }
831 
832  // Boundaries checking. TODO: How much of this should be done?
833  if (bytes_transferred > config::connection_read_buffer_size) {
834  m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
835  this->terminate(make_error_code(error::general));
836  return;
837  }
838 
839  size_t bytes_processed = 0;
840  try {
841  bytes_processed = m_request.consume(m_buf,bytes_transferred);
842  } catch (http::exception &e) {
843  // All HTTP exceptions will result in this request failing and an error
844  // response being returned. No more bytes will be read in this con.
845  m_response.set_status(e.m_error_code,e.m_error_msg);
846  this->write_http_response_error(error::make_error_code(error::http_parse_error));
847  return;
848  }
849 
850  // More paranoid boundaries checking.
851  // TODO: Is this overkill?
852  if (bytes_processed > bytes_transferred) {
853  m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
854  this->terminate(make_error_code(error::general));
855  return;
856  }
857 
858  if (m_alog.static_test(log::alevel::devel)) {
859  std::stringstream s;
860  s << "bytes_transferred: " << bytes_transferred
861  << " bytes, bytes processed: " << bytes_processed << " bytes";
862  m_alog.write(log::alevel::devel,s.str());
863  }
864 
865  if (m_request.ready()) {
866  lib::error_code processor_ec = this->initialize_processor();
867  if (processor_ec) {
868  this->write_http_response_error(processor_ec);
869  return;
870  }
871 
872  if (m_processor && m_processor->get_version() == 0) {
873  // Version 00 has an extra requirement to read some bytes after the
874  // handshake
875  if (bytes_transferred-bytes_processed >= 8) {
876  m_request.replace_header(
877  "Sec-WebSocket-Key3",
878  std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
879  );
880  bytes_processed += 8;
881  } else {
882  // TODO: need more bytes
883  m_alog.write(log::alevel::devel,"short key3 read");
884  m_response.set_status(http::status_code::internal_server_error);
885  this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3));
886  return;
887  }
888  }
889 
890  if (m_alog.static_test(log::alevel::devel)) {
891  m_alog.write(log::alevel::devel,m_request.raw());
892  if (m_request.get_header("Sec-WebSocket-Key3") != "") {
893  m_alog.write(log::alevel::devel,
894  utility::to_hex(m_request.get_header("Sec-WebSocket-Key3")));
895  }
896  }
897 
898  // The remaining bytes in m_buf are frame data. Copy them to the
899  // beginning of the buffer and note the length. They will be read after
900  // the handshake completes and before more bytes are read.
901  std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
902  m_buf_cursor = bytes_transferred-bytes_processed;
903 
904 
905  m_internal_state = istate::PROCESS_HTTP_REQUEST;
906 
907  // We have the complete request. Process it.
908  lib::error_code handshake_ec = this->process_handshake_request();
909  if (!m_is_http || m_http_state != session::http_state::deferred) {
910  this->write_http_response(handshake_ec);
911  }
912  } else {
913  // read at least 1 more byte
914  transport_con_type::async_read_at_least(
915  1,
916  m_buf,
917  config::connection_read_buffer_size,
918  lib::bind(
919  &type::handle_read_handshake,
920  type::get_shared(),
921  lib::placeholders::_1,
922  lib::placeholders::_2
923  )
924  );
925  }
926 }
927 
928 // write_http_response requires the request to be fully read and the connection
929 // to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors
930 // before the request is fully read (specifically at a point where we aren't
931 // sure if the hybi00 key3 bytes need to be read). This method sets the correct
932 // state and calls write_http_response
933 template <typename config>
934 void connection<config>::write_http_response_error(lib::error_code const & ec) {
935  if (m_internal_state != istate::READ_HTTP_REQUEST) {
936  m_alog.write(log::alevel::devel,
937  "write_http_response_error called in invalid state");
938  this->terminate(error::make_error_code(error::invalid_state));
939  return;
940  }
941 
942  m_internal_state = istate::PROCESS_HTTP_REQUEST;
943 
944  this->write_http_response(ec);
945 }
946 
947 // All exit paths for this function need to call write_http_response() or submit
948 // a new read request with this function as the handler.
949 template <typename config>
950 void connection<config>::handle_read_frame(lib::error_code const & ec,
951  size_t bytes_transferred)
952 {
953  //m_alog.write(log::alevel::devel,"connection handle_read_frame");
954 
955  lib::error_code ecm = ec;
956 
957  if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
958  ecm = error::make_error_code(error::invalid_state);
959  }
960 
961  if (ecm) {
962  log::level echannel = log::elevel::rerror;
963 
964  if (ecm == transport::error::eof) {
965  if (m_state == session::state::closed) {
966  // we expect to get eof if the connection is closed already
967  // just ignore it
968  m_alog.write(log::alevel::devel,"got eof from closed con");
969  return;
970  } else if (m_state == session::state::closing && !m_is_server) {
971  // If we are a client we expect to get eof in the closing state,
972  // this is a signal to terminate our end of the connection after
973  // the closing handshake
974  terminate(lib::error_code());
975  return;
976  }
977  } else if (ecm == error::invalid_state) {
978  // In general, invalid state errors in the closed state are the
979  // result of handlers that were in the system already when the state
980  // changed and should be ignored as they pose no problems and there
981  // is nothing useful that we can do about them.
982  if (m_state == session::state::closed) {
983  m_alog.write(log::alevel::devel,
984  "handle_read_frame: got invalid istate in closed state");
985  return;
986  }
987  } else if (ecm == transport::error::tls_short_read) {
988  if (m_state == session::state::closed) {
989  // We expect to get a TLS short read if we try to read after the
990  // connection is closed. If this happens ignore and exit the
991  // read frame path.
992  terminate(lib::error_code());
993  return;
994  }
995  echannel = log::elevel::rerror;
996  } else if (ecm == transport::error::action_after_shutdown) {
997  echannel = log::elevel::info;
998  }
999 
1000  log_err(echannel, "handle_read_frame", ecm);
1001  this->terminate(ecm);
1002  return;
1003  }
1004 
1005  // Boundaries checking. TODO: How much of this should be done?
1006  /*if (bytes_transferred > config::connection_read_buffer_size) {
1007  m_elog.write(log::elevel::fatal,"Fatal boundaries checking error");
1008  this->terminate(make_error_code(error::general));
1009  return;
1010  }*/
1011 
1012  size_t p = 0;
1013 
1014  if (m_alog.static_test(log::alevel::devel)) {
1015  std::stringstream s;
1016  s << "p = " << p << " bytes transferred = " << bytes_transferred;
1017  m_alog.write(log::alevel::devel,s.str());
1018  }
1019 
1020  while (p < bytes_transferred) {
1021  if (m_alog.static_test(log::alevel::devel)) {
1022  std::stringstream s;
1023  s << "calling consume with " << bytes_transferred-p << " bytes";
1024  m_alog.write(log::alevel::devel,s.str());
1025  }
1026 
1027  lib::error_code consume_ec;
1028 
1029  p += m_processor->consume(
1030  reinterpret_cast<uint8_t*>(m_buf)+p,
1031  bytes_transferred-p,
1032  consume_ec
1033  );
1034 
1035  if (m_alog.static_test(log::alevel::devel)) {
1036  std::stringstream s;
1037  s << "bytes left after consume: " << bytes_transferred-p;
1038  m_alog.write(log::alevel::devel,s.str());
1039  }
1040  if (consume_ec) {
1041  log_err(log::elevel::rerror, "consume", consume_ec);
1042 
1043  if (config::drop_on_protocol_error) {
1044  this->terminate(consume_ec);
1045  return;
1046  } else {
1047  lib::error_code close_ec;
1048  this->close(
1049  processor::error::to_ws(consume_ec),
1050  consume_ec.message(),
1051  close_ec
1052  );
1053 
1054  if (close_ec) {
1055  log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
1056  this->terminate(close_ec);
1057  return;
1058  }
1059  }
1060  return;
1061  }
1062 
1063  if (m_processor->ready()) {
1064  if (m_alog.static_test(log::alevel::devel)) {
1065  std::stringstream s;
1066  s << "Complete message received. Dispatching";
1067  m_alog.write(log::alevel::devel,s.str());
1068  }
1069 
1070  message_ptr msg = m_processor->get_message();
1071 
1072  if (!msg) {
1073  m_alog.write(log::alevel::devel, "null message from m_processor");
1074  } else if (!is_control(msg->get_opcode())) {
1075  // data message, dispatch to user
1076  if (m_state != session::state::open) {
1077  m_elog.write(log::elevel::warn, "got non-close frame while closing");
1078  } else if (m_message_handler) {
1079  m_message_handler(m_connection_hdl, msg);
1080  }
1081  } else {
1082  process_control_frame(msg);
1083  }
1084  }
1085  }
1086 
1087  read_frame();
1088 }
1089 
1091 template <typename config>
1093  if (!m_read_flag) {
1094  return;
1095  }
1096 
1097  transport_con_type::async_read_at_least(
1098  // std::min wont work with undefined static const values.
1099  // TODO: is there a more elegant way to do this?
1100  // Need to determine if requesting 1 byte or the exact number of bytes
1101  // is better here. 1 byte lets us be a bit more responsive at a
1102  // potential expense of additional runs through handle_read_frame
1103  /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
1104  config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
1105  1,
1106  m_buf,
1107  config::connection_read_buffer_size,
1108  m_handle_read_frame
1109  );
1110 }
1111 
1112 template <typename config>
1114  m_alog.write(log::alevel::devel,"initialize_processor");
1115 
1116  // if it isn't a websocket handshake nothing to do.
1117  if (!processor::is_websocket_handshake(m_request)) {
1118  return lib::error_code();
1119  }
1120 
1121  int version = processor::get_websocket_version(m_request);
1122 
1123  if (version < 0) {
1124  m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
1125  m_response.set_status(http::status_code::bad_request);
1126  return error::make_error_code(error::invalid_version);
1127  }
1128 
1129  m_processor = get_processor(version);
1130 
1131  // if the processor is not null we are done
1132  if (m_processor) {
1133  return lib::error_code();
1134  }
1135 
1136  // We don't have a processor for this version. Return bad request
1137  // with Sec-WebSocket-Version header filled with values we do accept
1138  m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version");
1139  m_response.set_status(http::status_code::bad_request);
1140 
1141  std::stringstream ss;
1142  std::string sep = "";
1143  std::vector<int>::const_iterator it;
1144  for (it = versions_supported.begin(); it != versions_supported.end(); it++)
1145  {
1146  ss << sep << *it;
1147  sep = ",";
1148  }
1149 
1150  m_response.replace_header("Sec-WebSocket-Version",ss.str());
1151  return error::make_error_code(error::unsupported_version);
1152 }
1153 
1154 template <typename config>
1156  m_alog.write(log::alevel::devel,"process handshake request");
1157 
1158  if (!processor::is_websocket_handshake(m_request)) {
1159  // this is not a websocket handshake. Process as plain HTTP
1160  m_alog.write(log::alevel::devel,"HTTP REQUEST");
1161 
1162  // extract URI from request
1164  m_request,
1165  (transport_con_type::is_secure() ? "https" : "http")
1166  );
1167 
1168  if (!m_uri->get_valid()) {
1169  m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
1170  m_response.set_status(http::status_code::bad_request);
1171  return error::make_error_code(error::invalid_uri);
1172  }
1173 
1174  if (m_http_handler) {
1175  m_is_http = true;
1176  m_http_handler(m_connection_hdl);
1177 
1178  if (m_state == session::state::closed) {
1179  return error::make_error_code(error::http_connection_ended);
1180  }
1181  } else {
1182  set_status(http::status_code::upgrade_required);
1183  return error::make_error_code(error::upgrade_required);
1184  }
1185 
1186  return lib::error_code();
1187  }
1188 
1189  lib::error_code ec = m_processor->validate_handshake(m_request);
1190 
1191  // Validate: make sure all required elements are present.
1192  if (ec){
1193  // Not a valid handshake request
1194  m_alog.write(log::alevel::devel, "Bad request " + ec.message());
1195  m_response.set_status(http::status_code::bad_request);
1196  return ec;
1197  }
1198 
1199  // Read extension parameters and set up values necessary for the end user
1200  // to complete extension negotiation.
1201  std::pair<lib::error_code,std::string> neg_results;
1202  neg_results = m_processor->negotiate_extensions(m_request);
1203 
1204  if (neg_results.first) {
1205  // There was a fatal error in extension parsing that should result in
1206  // a failed connection attempt.
1207  m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
1208  m_response.set_status(http::status_code::bad_request);
1209  return neg_results.first;
1210  } else {
1211  // extension negotiation succeeded, set response header accordingly
1212  // we don't send an empty extensions header because it breaks many
1213  // clients.
1214  if (neg_results.second.size() > 0) {
1215  m_response.replace_header("Sec-WebSocket-Extensions",
1216  neg_results.second);
1217  }
1218  }
1219 
1220  // extract URI from request
1221  m_uri = m_processor->get_uri(m_request);
1222 
1223 
1224  if (!m_uri->get_valid()) {
1225  m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
1226  m_response.set_status(http::status_code::bad_request);
1227  return error::make_error_code(error::invalid_uri);
1228  }
1229 
1230  // extract subprotocols
1231  lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1232  m_requested_subprotocols);
1233 
1234  if (subp_ec) {
1235  // should we do anything?
1236  }
1237 
1238  // Ask application to validate the connection
1239  if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1240  m_response.set_status(http::status_code::switching_protocols);
1241 
1242  // Write the appropriate response headers based on request and
1243  // processor version
1244  ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1245 
1246  if (ec) {
1247  std::stringstream s;
1248  s << "Processing error: " << ec << "(" << ec.message() << ")";
1249  m_alog.write(log::alevel::devel, s.str());
1250 
1251  m_response.set_status(http::status_code::internal_server_error);
1252  return ec;
1253  }
1254  } else {
1255  // User application has rejected the handshake
1256  m_alog.write(log::alevel::devel, "USER REJECT");
1257 
1258  // Use Bad Request if the user handler did not provide a more
1259  // specific http response error code.
1260  // TODO: is there a better default?
1261  if (m_response.get_status_code() == http::status_code::uninitialized) {
1262  m_response.set_status(http::status_code::bad_request);
1263  }
1264 
1265  return error::make_error_code(error::rejected);
1266  }
1267 
1268  return lib::error_code();
1269 }
1270 
1271 template <typename config>
1272 void connection<config>::write_http_response(lib::error_code const & ec) {
1273  m_alog.write(log::alevel::devel,"connection write_http_response");
1274 
1275  if (ec == error::make_error_code(error::http_connection_ended)) {
1276  m_alog.write(log::alevel::http,"An HTTP handler took over the connection.");
1277  return;
1278  }
1279 
1280  if (m_response.get_status_code() == http::status_code::uninitialized) {
1281  m_response.set_status(http::status_code::internal_server_error);
1282  m_ec = error::make_error_code(error::general);
1283  } else {
1284  m_ec = ec;
1285  }
1286 
1287  m_response.set_version("HTTP/1.1");
1288 
1289  // Set server header based on the user agent settings
1290  if (m_response.get_header("Server") == "") {
1291  if (!m_user_agent.empty()) {
1292  m_response.replace_header("Server",m_user_agent);
1293  } else {
1294  m_response.remove_header("Server");
1295  }
1296  }
1297 
1298  // have the processor generate the raw bytes for the wire (if it exists)
1299  if (m_processor) {
1300  m_handshake_buffer = m_processor->get_raw(m_response);
1301  } else {
1302  // a processor wont exist for raw HTTP responses.
1303  m_handshake_buffer = m_response.raw();
1304  }
1305 
1306  if (m_alog.static_test(log::alevel::devel)) {
1307  m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
1308  if (m_response.get_header("Sec-WebSocket-Key3") != "") {
1309  m_alog.write(log::alevel::devel,
1310  utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
1311  }
1312  }
1313 
1314  // write raw bytes
1315  transport_con_type::async_write(
1316  m_handshake_buffer.data(),
1317  m_handshake_buffer.size(),
1318  lib::bind(
1319  &type::handle_write_http_response,
1320  type::get_shared(),
1321  lib::placeholders::_1
1322  )
1323  );
1324 }
1325 
1326 template <typename config>
1327 void connection<config>::handle_write_http_response(lib::error_code const & ec) {
1328  m_alog.write(log::alevel::devel,"handle_write_http_response");
1329 
1330  lib::error_code ecm = ec;
1331 
1332  if (!ecm) {
1333  scoped_lock_type lock(m_connection_state_lock);
1334 
1335  if (m_state == session::state::connecting) {
1336  if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1337  ecm = error::make_error_code(error::invalid_state);
1338  }
1339  } else if (m_state == session::state::closed) {
1340  // The connection was canceled while the response was being sent,
1341  // usually by the handshake timer. This is basically expected
1342  // (though hopefully rare) and there is nothing we can do so ignore.
1343  m_alog.write(log::alevel::devel,
1344  "handle_write_http_response invoked after connection was closed");
1345  return;
1346  } else {
1347  ecm = error::make_error_code(error::invalid_state);
1348  }
1349  }
1350 
1351  if (ecm) {
1352  if (ecm == transport::error::eof && m_state == session::state::closed) {
1353  // we expect to get eof if the connection is closed already
1354  m_alog.write(log::alevel::devel,
1355  "got (expected) eof/state error from closed con");
1356  return;
1357  }
1358 
1359  log_err(log::elevel::rerror,"handle_write_http_response",ecm);
1360  this->terminate(ecm);
1361  return;
1362  }
1363 
1364  if (m_handshake_timer) {
1365  m_handshake_timer->cancel();
1366  m_handshake_timer.reset();
1367  }
1368 
1369  if (m_response.get_status_code() != http::status_code::switching_protocols)
1370  {
1371  /*if (m_processor || m_ec == error::http_parse_error ||
1372  m_ec == error::invalid_version || m_ec == error::unsupported_version
1373  || m_ec == error::upgrade_required)
1374  {*/
1375  if (!m_is_http) {
1376  std::stringstream s;
1377  s << "Handshake ended with HTTP error: "
1378  << m_response.get_status_code();
1379  m_elog.write(log::elevel::rerror,s.str());
1380  } else {
1381  // if this was not a websocket connection, we have written
1382  // the expected response and the connection can be closed.
1383 
1384  this->log_http_result();
1385 
1386  if (m_ec) {
1387  m_alog.write(log::alevel::devel,
1388  "got to writing HTTP results with m_ec set: "+m_ec.message());
1389  }
1390  m_ec = make_error_code(error::http_connection_ended);
1391  }
1392 
1393  this->terminate(m_ec);
1394  return;
1395  }
1396 
1397  this->log_open_result();
1398 
1399  m_internal_state = istate::PROCESS_CONNECTION;
1400  m_state = session::state::open;
1401 
1402  if (m_open_handler) {
1403  m_open_handler(m_connection_hdl);
1404  }
1405 
1406  this->handle_read_frame(lib::error_code(), m_buf_cursor);
1407 }
1408 
1409 template <typename config>
1410 void connection<config>::send_http_request() {
1411  m_alog.write(log::alevel::devel,"connection send_http_request");
1412 
1413  // TODO: origin header?
1414 
1415  // Have the protocol processor fill in the appropriate fields based on the
1416  // selected client version
1417  if (m_processor) {
1418  lib::error_code ec;
1419  ec = m_processor->client_handshake_request(m_request,m_uri,
1420  m_requested_subprotocols);
1421 
1422  if (ec) {
1423  log_err(log::elevel::fatal,"Internal library error: Processor",ec);
1424  return;
1425  }
1426  } else {
1427  m_elog.write(log::elevel::fatal,"Internal library error: missing processor");
1428  return;
1429  }
1430 
1431  // Unless the user has overridden the user agent, send generic WS++ UA.
1432  if (m_request.get_header("User-Agent") == "") {
1433  if (!m_user_agent.empty()) {
1434  m_request.replace_header("User-Agent",m_user_agent);
1435  } else {
1436  m_request.remove_header("User-Agent");
1437  }
1438  }
1439 
1440  m_handshake_buffer = m_request.raw();
1441 
1442  if (m_alog.static_test(log::alevel::devel)) {
1443  m_alog.write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
1444  }
1445 
1446  if (m_open_handshake_timeout_dur > 0) {
1447  m_handshake_timer = transport_con_type::set_timer(
1448  m_open_handshake_timeout_dur,
1449  lib::bind(
1450  &type::handle_open_handshake_timeout,
1451  type::get_shared(),
1452  lib::placeholders::_1
1453  )
1454  );
1455  }
1456 
1457  transport_con_type::async_write(
1458  m_handshake_buffer.data(),
1459  m_handshake_buffer.size(),
1460  lib::bind(
1461  &type::handle_send_http_request,
1462  type::get_shared(),
1463  lib::placeholders::_1
1464  )
1465  );
1466 }
1467 
1468 template <typename config>
1469 void connection<config>::handle_send_http_request(lib::error_code const & ec) {
1470  m_alog.write(log::alevel::devel,"handle_send_http_request");
1471 
1472  lib::error_code ecm = ec;
1473 
1474  if (!ecm) {
1475  scoped_lock_type lock(m_connection_state_lock);
1476 
1477  if (m_state == session::state::connecting) {
1478  if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1479  ecm = error::make_error_code(error::invalid_state);
1480  } else {
1481  m_internal_state = istate::READ_HTTP_RESPONSE;
1482  }
1483  } else if (m_state == session::state::closed) {
1484  // The connection was canceled while the response was being sent,
1485  // usually by the handshake timer. This is basically expected
1486  // (though hopefully rare) and there is nothing we can do so ignore.
1487  m_alog.write(log::alevel::devel,
1488  "handle_send_http_request invoked after connection was closed");
1489  return;
1490  } else {
1491  ecm = error::make_error_code(error::invalid_state);
1492  }
1493  }
1494 
1495  if (ecm) {
1496  if (ecm == transport::error::eof && m_state == session::state::closed) {
1497  // we expect to get eof if the connection is closed already
1498  m_alog.write(log::alevel::devel,
1499  "got (expected) eof/state error from closed con");
1500  return;
1501  }
1502 
1503  log_err(log::elevel::rerror,"handle_send_http_request",ecm);
1504  this->terminate(ecm);
1505  return;
1506  }
1507 
1508  transport_con_type::async_read_at_least(
1509  1,
1510  m_buf,
1511  config::connection_read_buffer_size,
1512  lib::bind(
1513  &type::handle_read_http_response,
1514  type::get_shared(),
1515  lib::placeholders::_1,
1516  lib::placeholders::_2
1517  )
1518  );
1519 }
1520 
1521 template <typename config>
1522 void connection<config>::handle_read_http_response(lib::error_code const & ec,
1523  size_t bytes_transferred)
1524 {
1525  m_alog.write(log::alevel::devel,"handle_read_http_response");
1526 
1527  lib::error_code ecm = ec;
1528 
1529  if (!ecm) {
1530  scoped_lock_type lock(m_connection_state_lock);
1531 
1532  if (m_state == session::state::connecting) {
1533  if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1534  ecm = error::make_error_code(error::invalid_state);
1535  }
1536  } else if (m_state == session::state::closed) {
1537  // The connection was canceled while the response was being sent,
1538  // usually by the handshake timer. This is basically expected
1539  // (though hopefully rare) and there is nothing we can do so ignore.
1540  m_alog.write(log::alevel::devel,
1541  "handle_read_http_response invoked after connection was closed");
1542  return;
1543  } else {
1544  ecm = error::make_error_code(error::invalid_state);
1545  }
1546  }
1547 
1548  if (ecm) {
1549  if (ecm == transport::error::eof && m_state == session::state::closed) {
1550  // we expect to get eof if the connection is closed already
1551  m_alog.write(log::alevel::devel,
1552  "got (expected) eof/state error from closed con");
1553  return;
1554  }
1555 
1556  log_err(log::elevel::rerror,"handle_read_http_response",ecm);
1557  this->terminate(ecm);
1558  return;
1559  }
1560 
1561  size_t bytes_processed = 0;
1562  // TODO: refactor this to use error codes rather than exceptions
1563  try {
1564  bytes_processed = m_response.consume(m_buf,bytes_transferred);
1565  } catch (http::exception & e) {
1566  m_elog.write(log::elevel::rerror,
1567  std::string("error in handle_read_http_response: ")+e.what());
1568  this->terminate(make_error_code(error::general));
1569  return;
1570  }
1571 
1572  m_alog.write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
1573 
1574  if (m_response.headers_ready()) {
1575  if (m_handshake_timer) {
1576  m_handshake_timer->cancel();
1577  m_handshake_timer.reset();
1578  }
1579 
1580  lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1581  m_request,
1582  m_response
1583  );
1584  if (validate_ec) {
1585  log_err(log::elevel::rerror,"Server handshake response",validate_ec);
1586  this->terminate(validate_ec);
1587  return;
1588  }
1589 
1590  // response is valid, connection can now be assumed to be open
1591  m_internal_state = istate::PROCESS_CONNECTION;
1592  m_state = session::state::open;
1593 
1594  this->log_open_result();
1595 
1596  if (m_open_handler) {
1597  m_open_handler(m_connection_hdl);
1598  }
1599 
1600  // The remaining bytes in m_buf are frame data. Copy them to the
1601  // beginning of the buffer and note the length. They will be read after
1602  // the handshake completes and before more bytes are read.
1603  std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1604  m_buf_cursor = bytes_transferred-bytes_processed;
1605 
1606  this->handle_read_frame(lib::error_code(), m_buf_cursor);
1607  } else {
1608  transport_con_type::async_read_at_least(
1609  1,
1610  m_buf,
1611  config::connection_read_buffer_size,
1612  lib::bind(
1613  &type::handle_read_http_response,
1614  type::get_shared(),
1615  lib::placeholders::_1,
1616  lib::placeholders::_2
1617  )
1618  );
1619  }
1620 }
1621 
1622 template <typename config>
1623 void connection<config>::handle_open_handshake_timeout(
1624  lib::error_code const & ec)
1625 {
1627  m_alog.write(log::alevel::devel,"open handshake timer cancelled");
1628  } else if (ec) {
1629  m_alog.write(log::alevel::devel,
1630  "open handle_open_handshake_timeout error: "+ec.message());
1631  // TODO: ignore or fail here?
1632  } else {
1633  m_alog.write(log::alevel::devel,"open handshake timer expired");
1634  terminate(make_error_code(error::open_handshake_timeout));
1635  }
1636 }
1637 
1638 template <typename config>
1639 void connection<config>::handle_close_handshake_timeout(
1640  lib::error_code const & ec)
1641 {
1643  m_alog.write(log::alevel::devel,"asio close handshake timer cancelled");
1644  } else if (ec) {
1645  m_alog.write(log::alevel::devel,
1646  "asio open handle_close_handshake_timeout error: "+ec.message());
1647  // TODO: ignore or fail here?
1648  } else {
1649  m_alog.write(log::alevel::devel, "asio close handshake timer expired");
1650  terminate(make_error_code(error::close_handshake_timeout));
1651  }
1652 }
1653 
1654 template <typename config>
1655 void connection<config>::terminate(lib::error_code const & ec) {
1656  if (m_alog.static_test(log::alevel::devel)) {
1657  m_alog.write(log::alevel::devel,"connection terminate");
1658  }
1659 
1660  // Cancel close handshake timer
1661  if (m_handshake_timer) {
1662  m_handshake_timer->cancel();
1663  m_handshake_timer.reset();
1664  }
1665 
1666  terminate_status tstat = unknown;
1667  if (ec) {
1668  m_ec = ec;
1669  m_local_close_code = close::status::abnormal_close;
1670  m_local_close_reason = ec.message();
1671  }
1672 
1673  // TODO: does any of this need a mutex?
1674  if (m_is_http) {
1675  m_http_state = session::http_state::closed;
1676  }
1677  if (m_state == session::state::connecting) {
1678  m_state = session::state::closed;
1679  tstat = failed;
1680 
1681  // Log fail result here before socket is shut down and we can't get
1682  // the remote address, etc anymore
1683  if (m_ec != error::http_connection_ended) {
1684  log_fail_result();
1685  }
1686  } else if (m_state != session::state::closed) {
1687  m_state = session::state::closed;
1688  tstat = closed;
1689  } else {
1690  m_alog.write(log::alevel::devel,
1691  "terminate called on connection that was already terminated");
1692  return;
1693  }
1694 
1695  // TODO: choose between shutdown and close based on error code sent
1696 
1697  transport_con_type::async_shutdown(
1698  lib::bind(
1699  &type::handle_terminate,
1700  type::get_shared(),
1701  tstat,
1702  lib::placeholders::_1
1703  )
1704  );
1705 }
1706 
1707 template <typename config>
1708 void connection<config>::handle_terminate(terminate_status tstat,
1709  lib::error_code const & ec)
1710 {
1711  if (m_alog.static_test(log::alevel::devel)) {
1712  m_alog.write(log::alevel::devel,"connection handle_terminate");
1713  }
1714 
1715  if (ec) {
1716  // there was an error actually shutting down the connection
1717  log_err(log::elevel::devel,"handle_terminate",ec);
1718  }
1719 
1720  // clean shutdown
1721  if (tstat == failed) {
1722  if (m_ec != error::http_connection_ended) {
1723  if (m_fail_handler) {
1724  m_fail_handler(m_connection_hdl);
1725  }
1726  }
1727  } else if (tstat == closed) {
1728  if (m_close_handler) {
1729  m_close_handler(m_connection_hdl);
1730  }
1731  log_close_result();
1732  } else {
1733  m_elog.write(log::elevel::rerror,"Unknown terminate_status");
1734  }
1735 
1736  // call the termination handler if it exists
1737  // if it exists it might (but shouldn't) refer to a bad memory location.
1738  // If it does, we don't care and should catch and ignore it.
1739  if (m_termination_handler) {
1740  try {
1741  m_termination_handler(type::get_shared());
1742  } catch (std::exception const & e) {
1743  m_elog.write(log::elevel::warn,
1744  std::string("termination_handler call failed. Reason was: ")+e.what());
1745  }
1746  }
1747 }
1748 
1749 template <typename config>
1751  //m_alog.write(log::alevel::devel,"connection write_frame");
1752 
1753  {
1754  scoped_lock_type lock(m_write_lock);
1755 
1756  // Check the write flag. If true, there is an outstanding transport
1757  // write already. In this case we just return. The write handler will
1758  // start a new write if the write queue isn't empty. If false, we set
1759  // the write flag and proceed to initiate a transport write.
1760  if (m_write_flag) {
1761  return;
1762  }
1763 
1764  // pull off all the messages that are ready to write.
1765  // stop if we get a message marked terminal
1766  message_ptr next_message = write_pop();
1767  while (next_message) {
1768  m_current_msgs.push_back(next_message);
1769  if (!next_message->get_terminal()) {
1770  next_message = write_pop();
1771  } else {
1772  next_message = message_ptr();
1773  }
1774  }
1775 
1776  if (m_current_msgs.empty()) {
1777  // there was nothing to send
1778  return;
1779  } else {
1780  // At this point we own the next messages to be sent and are
1781  // responsible for holding the write flag until they are
1782  // successfully sent or there is some error
1783  m_write_flag = true;
1784  }
1785  }
1786 
1787  typename std::vector<message_ptr>::iterator it;
1788  for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1789  std::string const & header = (*it)->get_header();
1790  std::string const & payload = (*it)->get_payload();
1791 
1792  m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
1793  m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
1794  }
1795 
1796  // Print detailed send stats if those log levels are enabled
1797  if (m_alog.static_test(log::alevel::frame_header)) {
1798  if (m_alog.dynamic_test(log::alevel::frame_header)) {
1799  std::stringstream general,header,payload;
1800 
1801  general << "Dispatching write containing " << m_current_msgs.size()
1802  <<" message(s) containing ";
1803  header << "Header Bytes: \n";
1804  payload << "Payload Bytes: \n";
1805 
1806  size_t hbytes = 0;
1807  size_t pbytes = 0;
1808 
1809  for (size_t i = 0; i < m_current_msgs.size(); i++) {
1810  hbytes += m_current_msgs[i]->get_header().size();
1811  pbytes += m_current_msgs[i]->get_payload().size();
1812 
1813 
1814  header << "[" << i << "] ("
1815  << m_current_msgs[i]->get_header().size() << ") "
1816  << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
1817 
1818  if (m_alog.static_test(log::alevel::frame_payload)) {
1819  if (m_alog.dynamic_test(log::alevel::frame_payload)) {
1820  payload << "[" << i << "] ("
1821  << m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
1822  << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
1823  m_current_msgs[i]->get_payload() :
1824  utility::to_hex(m_current_msgs[i]->get_payload())
1825  )
1826  << "\n";
1827  }
1828  }
1829  }
1830 
1831  general << hbytes << " header bytes and " << pbytes << " payload bytes";
1832 
1833  m_alog.write(log::alevel::frame_header,general.str());
1834  m_alog.write(log::alevel::frame_header,header.str());
1835  m_alog.write(log::alevel::frame_payload,payload.str());
1836  }
1837  }
1838 
1839  transport_con_type::async_write(
1840  m_send_buffer,
1841  m_write_frame_handler
1842  );
1843 }
1844 
1845 template <typename config>
1846 void connection<config>::handle_write_frame(lib::error_code const & ec)
1847 {
1848  if (m_alog.static_test(log::alevel::devel)) {
1849  m_alog.write(log::alevel::devel,"connection handle_write_frame");
1850  }
1851 
1852  bool terminal = m_current_msgs.back()->get_terminal();
1853 
1854  m_send_buffer.clear();
1855  m_current_msgs.clear();
1856  // TODO: recycle instead of deleting
1857 
1858  if (ec) {
1859  log_err(log::elevel::fatal,"handle_write_frame",ec);
1860  this->terminate(ec);
1861  return;
1862  }
1863 
1864  if (terminal) {
1865  this->terminate(lib::error_code());
1866  return;
1867  }
1868 
1869  bool needs_writing = false;
1870  {
1871  scoped_lock_type lock(m_write_lock);
1872 
1873  // release write flag
1874  m_write_flag = false;
1875 
1876  needs_writing = !m_send_queue.empty();
1877  }
1878 
1879  if (needs_writing) {
1880  transport_con_type::dispatch(lib::bind(
1881  &type::write_frame,
1882  type::get_shared()
1883  ));
1884  }
1885 }
1886 
1887 template <typename config>
1888 std::vector<int> const & connection<config>::get_supported_versions() const
1889 {
1890  return versions_supported;
1891 }
1892 
1893 template <typename config>
1894 void connection<config>::process_control_frame(typename config::message_type::ptr msg)
1895 {
1896  m_alog.write(log::alevel::devel,"process_control_frame");
1897 
1898  frame::opcode::value op = msg->get_opcode();
1899  lib::error_code ec;
1900 
1901  std::stringstream s;
1902  s << "Control frame received with opcode " << op;
1903  m_alog.write(log::alevel::control,s.str());
1904 
1905  if (m_state == session::state::closed) {
1906  m_elog.write(log::elevel::warn,"got frame in state closed");
1907  return;
1908  }
1909  if (op != frame::opcode::CLOSE && m_state != session::state::open) {
1910  m_elog.write(log::elevel::warn,"got non-close frame in state closing");
1911  return;
1912  }
1913 
1914  if (op == frame::opcode::PING) {
1915  bool should_reply = true;
1916 
1917  if (m_ping_handler) {
1918  should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
1919  }
1920 
1921  if (should_reply) {
1922  this->pong(msg->get_payload(),ec);
1923  if (ec) {
1924  log_err(log::elevel::devel,"Failed to send response pong",ec);
1925  }
1926  }
1927  } else if (op == frame::opcode::PONG) {
1928  if (m_pong_handler) {
1929  m_pong_handler(m_connection_hdl, msg->get_payload());
1930  }
1931  if (m_ping_timer) {
1932  m_ping_timer->cancel();
1933  }
1934  } else if (op == frame::opcode::CLOSE) {
1935  m_alog.write(log::alevel::devel,"got close frame");
1936  // record close code and reason somewhere
1937 
1938  m_remote_close_code = close::extract_code(msg->get_payload(),ec);
1939  if (ec) {
1940  s.str("");
1941  if (config::drop_on_protocol_error) {
1942  s << "Received invalid close code " << m_remote_close_code
1943  << " dropping connection per config.";
1944  m_elog.write(log::elevel::devel,s.str());
1945  this->terminate(ec);
1946  } else {
1947  s << "Received invalid close code " << m_remote_close_code
1948  << " sending acknowledgement and closing";
1949  m_elog.write(log::elevel::devel,s.str());
1950  ec = send_close_ack(close::status::protocol_error,
1951  "Invalid close code");
1952  if (ec) {
1953  log_err(log::elevel::devel,"send_close_ack",ec);
1954  }
1955  }
1956  return;
1957  }
1958 
1959  m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
1960  if (ec) {
1961  if (config::drop_on_protocol_error) {
1962  m_elog.write(log::elevel::devel,
1963  "Received invalid close reason. Dropping connection per config");
1964  this->terminate(ec);
1965  } else {
1966  m_elog.write(log::elevel::devel,
1967  "Received invalid close reason. Sending acknowledgement and closing");
1968  ec = send_close_ack(close::status::protocol_error,
1969  "Invalid close reason");
1970  if (ec) {
1971  log_err(log::elevel::devel,"send_close_ack",ec);
1972  }
1973  }
1974  return;
1975  }
1976 
1977  if (m_state == session::state::open) {
1978  s.str("");
1979  s << "Received close frame with code " << m_remote_close_code
1980  << " and reason " << m_remote_close_reason;
1981  m_alog.write(log::alevel::devel,s.str());
1982 
1983  ec = send_close_ack();
1984  if (ec) {
1985  log_err(log::elevel::devel,"send_close_ack",ec);
1986  }
1987  } else if (m_state == session::state::closing && !m_was_clean) {
1988  // ack of our close
1989  m_alog.write(log::alevel::devel, "Got acknowledgement of close");
1990 
1991  m_was_clean = true;
1992 
1993  // If we are a server terminate the connection now. Clients should
1994  // leave the connection open to give the server an opportunity to
1995  // initiate the TCP close. The client's timer will handle closing
1996  // its side of the connection if the server misbehaves.
1997  //
1998  // TODO: different behavior if the underlying transport doesn't
1999  // support timers?
2000  if (m_is_server) {
2001  terminate(lib::error_code());
2002  }
2003  } else {
2004  // spurious, ignore
2005  m_elog.write(log::elevel::devel, "Got close frame in wrong state");
2006  }
2007  } else {
2008  // got an invalid control opcode
2009  m_elog.write(log::elevel::devel, "Got control frame with invalid opcode");
2010  // initiate protocol error shutdown
2011  }
2012 }
2013 
2014 template <typename config>
2015 lib::error_code connection<config>::send_close_ack(close::status::value code,
2016  std::string const & reason)
2017 {
2018  return send_close_frame(code,reason,true,m_is_server);
2019 }
2020 
2021 template <typename config>
2022 lib::error_code connection<config>::send_close_frame(close::status::value code,
2023  std::string const & reason, bool ack, bool terminal)
2024 {
2025  m_alog.write(log::alevel::devel,"send_close_frame");
2026 
2027  // check for special codes
2028 
2029  // If silent close is set, respect it and blank out close information
2030  // Otherwise use whatever has been specified in the parameters. If
2031  // parameters specifies close::status::blank then determine what to do
2032  // based on whether or not this is an ack. If it is not an ack just
2033  // send blank info. If it is an ack then echo the close information from
2034  // the remote endpoint.
2035  if (config::silent_close) {
2036  m_alog.write(log::alevel::devel,"closing silently");
2037  m_local_close_code = close::status::no_status;
2038  m_local_close_reason = "";
2039  } else if (code != close::status::blank) {
2040  m_alog.write(log::alevel::devel,"closing with specified codes");
2041  m_local_close_code = code;
2042  m_local_close_reason = reason;
2043  } else if (!ack) {
2044  m_alog.write(log::alevel::devel,"closing with no status code");
2045  m_local_close_code = close::status::no_status;
2046  m_local_close_reason = "";
2047  } else if (m_remote_close_code == close::status::no_status) {
2048  m_alog.write(log::alevel::devel,
2049  "acknowledging a no-status close with normal code");
2050  m_local_close_code = close::status::normal;
2051  m_local_close_reason = "";
2052  } else {
2053  m_alog.write(log::alevel::devel,"acknowledging with remote codes");
2054  m_local_close_code = m_remote_close_code;
2055  m_local_close_reason = m_remote_close_reason;
2056  }
2057 
2058  std::stringstream s;
2059  s << "Closing with code: " << m_local_close_code << ", and reason: "
2060  << m_local_close_reason;
2061  m_alog.write(log::alevel::devel,s.str());
2062 
2063  message_ptr msg = m_msg_manager->get_message();
2064  if (!msg) {
2065  return error::make_error_code(error::no_outgoing_buffers);
2066  }
2067 
2068  lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2069  m_local_close_reason,msg);
2070  if (ec) {
2071  return ec;
2072  }
2073 
2074  // Messages flagged terminal will result in the TCP connection being dropped
2075  // after the message has been written. This is typically used when servers
2076  // send an ack and when any endpoint encounters a protocol error
2077  if (terminal) {
2078  msg->set_terminal(true);
2079  }
2080 
2081  m_state = session::state::closing;
2082 
2083  if (ack) {
2084  m_was_clean = true;
2085  }
2086 
2087  // Start a timer so we don't wait forever for the acknowledgement close
2088  // frame
2089  if (m_close_handshake_timeout_dur > 0) {
2090  m_handshake_timer = transport_con_type::set_timer(
2091  m_close_handshake_timeout_dur,
2092  lib::bind(
2093  &type::handle_close_handshake_timeout,
2094  type::get_shared(),
2095  lib::placeholders::_1
2096  )
2097  );
2098  }
2099 
2100  bool needs_writing = false;
2101  {
2102  scoped_lock_type lock(m_write_lock);
2103  write_push(msg);
2104  needs_writing = !m_write_flag && !m_send_queue.empty();
2105  }
2106 
2107  if (needs_writing) {
2108  transport_con_type::dispatch(lib::bind(
2109  &type::write_frame,
2110  type::get_shared()
2111  ));
2112  }
2113 
2114  return lib::error_code();
2115 }
2116 
2117 template <typename config>
2118 typename connection<config>::processor_ptr
2119 connection<config>::get_processor(int version) const {
2120  // TODO: allow disabling certain versions
2121 
2122  processor_ptr p;
2123 
2124  switch (version) {
2125  case 0:
2126  p = lib::make_shared<processor::hybi00<config> >(
2127  transport_con_type::is_secure(),
2128  m_is_server,
2129  m_msg_manager
2130  );
2131  break;
2132  case 7:
2133  p = lib::make_shared<processor::hybi07<config> >(
2134  transport_con_type::is_secure(),
2135  m_is_server,
2136  m_msg_manager,
2137  lib::ref(m_rng)
2138  );
2139  break;
2140  case 8:
2141  p = lib::make_shared<processor::hybi08<config> >(
2142  transport_con_type::is_secure(),
2143  m_is_server,
2144  m_msg_manager,
2145  lib::ref(m_rng)
2146  );
2147  break;
2148  case 13:
2149  p = lib::make_shared<processor::hybi13<config> >(
2150  transport_con_type::is_secure(),
2151  m_is_server,
2152  m_msg_manager,
2153  lib::ref(m_rng)
2154  );
2155  break;
2156  default:
2157  return p;
2158  }
2159 
2160  // Settings not configured by the constructor
2161  p->set_max_message_size(m_max_message_size);
2162 
2163  return p;
2164 }
2165 
2166 template <typename config>
2167 void connection<config>::write_push(typename config::message_type::ptr msg)
2168 {
2169  if (!msg) {
2170  return;
2171  }
2172 
2173  m_send_buffer_size += msg->get_payload().size();
2174  m_send_queue.push(msg);
2175 
2176  if (m_alog.static_test(log::alevel::devel)) {
2177  std::stringstream s;
2178  s << "write_push: message count: " << m_send_queue.size()
2179  << " buffer size: " << m_send_buffer_size;
2180  m_alog.write(log::alevel::devel,s.str());
2181  }
2182 }
2183 
2184 template <typename config>
2185 typename config::message_type::ptr connection<config>::write_pop()
2186 {
2187  message_ptr msg;
2188 
2189  if (m_send_queue.empty()) {
2190  return msg;
2191  }
2192 
2193  msg = m_send_queue.front();
2194 
2195  m_send_buffer_size -= msg->get_payload().size();
2196  m_send_queue.pop();
2197 
2198  if (m_alog.static_test(log::alevel::devel)) {
2199  std::stringstream s;
2200  s << "write_pop: message count: " << m_send_queue.size()
2201  << " buffer size: " << m_send_buffer_size;
2202  m_alog.write(log::alevel::devel,s.str());
2203  }
2204  return msg;
2205 }
2206 
2207 template <typename config>
2208 void connection<config>::log_open_result()
2209 {
2210  std::stringstream s;
2211 
2212  int version;
2213  if (!processor::is_websocket_handshake(m_request)) {
2214  version = -1;
2215  } else {
2216  version = processor::get_websocket_version(m_request);
2217  }
2218 
2219  // Connection Type
2220  s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
2221 
2222  // Remote endpoint address
2223  s << transport_con_type::get_remote_endpoint() << " ";
2224 
2225  // Version string if WebSocket
2226  if (version != -1) {
2227  s << "v" << version << " ";
2228  }
2229 
2230  // User Agent
2231  std::string ua = m_request.get_header("User-Agent");
2232  if (ua == "") {
2233  s << "\"\" ";
2234  } else {
2235  // check if there are any quotes in the user agent
2236  s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2237  }
2238 
2239  // URI
2240  s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
2241 
2242  // Status code
2243  s << m_response.get_status_code();
2244 
2245  m_alog.write(log::alevel::connect,s.str());
2246 }
2247 
2248 template <typename config>
2249 void connection<config>::log_close_result()
2250 {
2251  std::stringstream s;
2252 
2253  s << "Disconnect "
2254  << "close local:[" << m_local_close_code
2255  << (m_local_close_reason == "" ? "" : ","+m_local_close_reason)
2256  << "] remote:[" << m_remote_close_code
2257  << (m_remote_close_reason == "" ? "" : ","+m_remote_close_reason) << "]";
2258 
2259  m_alog.write(log::alevel::disconnect,s.str());
2260 }
2261 
2262 template <typename config>
2263 void connection<config>::log_fail_result()
2264 {
2265  std::stringstream s;
2266 
2267  int version = processor::get_websocket_version(m_request);
2268 
2269  // Connection Type
2270  s << "WebSocket Connection ";
2271 
2272  // Remote endpoint address & WebSocket version
2273  s << transport_con_type::get_remote_endpoint();
2274  if (version < 0) {
2275  s << " -";
2276  } else {
2277  s << " v" << version;
2278  }
2279 
2280  // User Agent
2281  std::string ua = m_request.get_header("User-Agent");
2282  if (ua == "") {
2283  s << " \"\" ";
2284  } else {
2285  // check if there are any quotes in the user agent
2286  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2287  }
2288 
2289  // URI
2290  s << (m_uri ? m_uri->get_resource() : "-");
2291 
2292  // HTTP Status code
2293  s << " " << m_response.get_status_code();
2294 
2295  // WebSocket++ error code & reason
2296  s << " " << m_ec << " " << m_ec.message();
2297 
2298  m_alog.write(log::alevel::fail,s.str());
2299 }
2300 
2301 template <typename config>
2302 void connection<config>::log_http_result() {
2303  std::stringstream s;
2304 
2305  if (processor::is_websocket_handshake(m_request)) {
2306  m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket");
2307  return;
2308  }
2309 
2310  // Connection Type
2311  s << (m_request.get_header("host") == "" ? "-" : m_request.get_header("host"))
2312  << " " << transport_con_type::get_remote_endpoint()
2313  << " \"" << m_request.get_method()
2314  << " " << (m_uri ? m_uri->get_resource() : "-")
2315  << " " << m_request.get_version() << "\" " << m_response.get_status_code()
2316  << " " << m_response.get_body().size();
2317 
2318  // User Agent
2319  std::string ua = m_request.get_header("User-Agent");
2320  if (ua == "") {
2321  s << " \"\" ";
2322  } else {
2323  // check if there are any quotes in the user agent
2324  s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2325  }
2326 
2327  m_alog.write(log::alevel::http,s.str());
2328 }
2329 
2330 } // namespace websocketpp
2331 
2332 #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:235
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.
void send_http_response()
Send deferred HTTP Response.
bool is_not_token_char(unsigned char c)
Is the character a non-token.
Definition: constants.hpp:103
void handle_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.
lib::error_code defer_http_response()
Defer HTTP Response until later (Exception free)
Invalid WebSocket protocol version.
Definition: error.hpp:137
void set_status(http::status_code::value code)
Set response status code and message.
void handle_pong_timeout(std::string payload, lib::error_code const &ec)
Utility method that gets called back when the ping timer expires.
close::status::value to_ws(lib::error_code ec)
Converts a processor error_code into a websocket close code.
Definition: base.hpp:261
lib::error_code make_error_code(error::processor_errors e)
Create an error code with the given value and the processor category.
Definition: base.hpp:244
void append_header(std::string const &key, std::string const &val)
Append a header.
lib::error_code send(std::string const &payload, frame::opcode::value op=frame::opcode::text)
Create a message and then add it to the outgoing send queue.
The connection was in the wrong state for this operation.
Definition: error.hpp:74
static level const info
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.