WebSocket++ 0.8.4
C++ websocket client/server library
Loading...
Searching...
No Matches
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
48namespace websocketpp {
49
50namespace istate = session::internal_state;
51
52template <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
64template <typename config>
65std::string const & connection<config>::get_origin() const {
66 //scoped_lock_type lock(m_connection_state_lock);
67 if (!m_processor) {
68 return http::empty_header;
69 }
70 return m_processor->get_origin(m_request);
71}
72
73template <typename config>
75 //scoped_lock_type lock(m_connection_state_lock);
76 return m_send_buffer_size;
77}
78
79template <typename config>
80session::state::value connection<config>::get_state() const {
81 //scoped_lock_type lock(m_connection_state_lock);
82 return m_state;
83}
84
85template <typename config>
86lib::error_code connection<config>::send(std::string const & payload,
87 frame::opcode::value op)
88{
89 message_ptr msg = m_msg_manager->get_message(op,payload.size());
90 msg->append_payload(payload);
91 msg->set_compressed(true);
92
93 return send(msg);
94}
95
96template <typename config>
97lib::error_code connection<config>::send(void const * payload, size_t len,
98 frame::opcode::value op)
99{
100 message_ptr msg = m_msg_manager->get_message(op,len);
101 msg->append_payload(payload,len);
102
103 return send(msg);
104}
105
106template <typename config>
107lib::error_code connection<config>::send(typename config::message_type::ptr msg)
108{
109 if (m_alog->static_test(log::alevel::devel)) {
110 m_alog->write(log::alevel::devel,"connection send");
111 }
112
113 {
114 scoped_lock_type lock(m_connection_state_lock);
115 if (m_state != session::state::open) {
116 return error::make_error_code(error::invalid_state);
117 }
118 }
119
120 message_ptr outgoing_msg;
121 bool needs_writing = false;
122
123 if (msg->get_prepared()) {
124 outgoing_msg = msg;
125
126 scoped_lock_type lock(m_write_lock);
127 write_push(outgoing_msg);
128 needs_writing = !m_write_flag && !m_send_queue.empty();
129 } else {
130 outgoing_msg = m_msg_manager->get_message();
131
132 if (!outgoing_msg) {
133 return error::make_error_code(error::no_outgoing_buffers);
134 }
135
136 scoped_lock_type lock(m_write_lock);
137 lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg);
138
139 if (ec) {
140 return ec;
141 }
142
143 write_push(outgoing_msg);
144 needs_writing = !m_write_flag && !m_send_queue.empty();
145 }
146
147 if (needs_writing) {
148 transport_con_type::dispatch(lib::bind(
151 ));
152 }
153
154 return lib::error_code();
155}
156
157template <typename config>
158void connection<config>::ping(std::string const& payload, lib::error_code& ec) {
159 if (m_alog->static_test(log::alevel::devel)) {
160 m_alog->write(log::alevel::devel,"connection ping");
161 }
162
163 {
164 scoped_lock_type lock(m_connection_state_lock);
165 if (m_state != session::state::open) {
166 std::stringstream ss;
167 ss << "connection::ping called from invalid state " << m_state;
168 m_alog->write(log::alevel::devel,ss.str());
169 ec = error::make_error_code(error::invalid_state);
170 return;
171 }
172 }
173
174 message_ptr msg = m_msg_manager->get_message();
175 if (!msg) {
176 ec = error::make_error_code(error::no_outgoing_buffers);
177 return;
178 }
179
180 ec = m_processor->prepare_ping(payload,msg);
181 if (ec) {return;}
182
183 // set ping timer if we are listening for one
184 if (m_pong_timeout_handler) {
185 // Cancel any existing timers
186 if (m_ping_timer) {
187 m_ping_timer->cancel();
188 }
189
190 if (m_pong_timeout_dur > 0) {
191 m_ping_timer = transport_con_type::set_timer(
192 m_pong_timeout_dur,
193 lib::bind(
194 &type::handle_pong_timeout,
196 payload,
197 lib::placeholders::_1
198 )
199 );
200 }
201
202 if (!m_ping_timer) {
203 // Our transport doesn't support timers
204 m_elog->write(log::elevel::warn,"Warning: a pong_timeout_handler is \
205 set but the transport in use does not support timeouts.");
206 }
207 }
208
209 bool needs_writing = false;
210 {
211 scoped_lock_type lock(m_write_lock);
212 write_push(msg);
213 needs_writing = !m_write_flag && !m_send_queue.empty();
214 }
215
216 if (needs_writing) {
217 transport_con_type::dispatch(lib::bind(
220 ));
221 }
222
223 ec = lib::error_code();
224}
225
226#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
227template<typename config>
228void connection<config>::ping(std::string const & payload) {
229 lib::error_code ec;
230 ping(payload,ec);
231 if (ec) {
232 throw exception(ec);
233 }
234}
235#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
236
237template<typename config>
238void connection<config>::handle_pong_timeout(std::string payload,
239 lib::error_code const & ec)
240{
241 if (ec) {
243 // ignore, this is expected
244 return;
245 }
246
247 m_elog->write(log::elevel::devel,"pong_timeout error: "+ec.message());
248 return;
249 }
250
251 if (m_pong_timeout_handler) {
252 m_pong_timeout_handler(m_connection_hdl,payload);
253 }
254}
255
256template <typename config>
257void connection<config>::pong(std::string const& payload, lib::error_code& ec) {
258 if (m_alog->static_test(log::alevel::devel)) {
259 m_alog->write(log::alevel::devel,"connection pong");
260 }
261
262 {
263 scoped_lock_type lock(m_connection_state_lock);
264 if (m_state != session::state::open) {
265 std::stringstream ss;
266 ss << "connection::pong called from invalid state " << m_state;
267 m_alog->write(log::alevel::devel,ss.str());
268 ec = error::make_error_code(error::invalid_state);
269 return;
270 }
271 }
272
273 message_ptr msg = m_msg_manager->get_message();
274 if (!msg) {
275 ec = error::make_error_code(error::no_outgoing_buffers);
276 return;
277 }
278
279 ec = m_processor->prepare_pong(payload,msg);
280 if (ec) {return;}
281
282 bool needs_writing = false;
283 {
284 scoped_lock_type lock(m_write_lock);
285 write_push(msg);
286 needs_writing = !m_write_flag && !m_send_queue.empty();
287 }
288
289 if (needs_writing) {
290 transport_con_type::dispatch(lib::bind(
293 ));
294 }
295
296 ec = lib::error_code();
297}
298
299#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
300template<typename config>
301void connection<config>::pong(std::string const & payload) {
302 lib::error_code ec;
303 pong(payload,ec);
304 if (ec) {
305 throw exception(ec);
306 }
307}
308#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
309
310template <typename config>
311void connection<config>::close(close::status::value const code,
312 std::string const & reason, lib::error_code & ec)
313{
314 if (m_alog->static_test(log::alevel::devel)) {
315 m_alog->write(log::alevel::devel,"connection close");
316 }
317
318 // Truncate reason to maximum size allowable in a close frame.
319 std::string tr(reason,0,std::min<size_t>(reason.size(),
320 frame::limits::close_reason_size));
321
322 scoped_lock_type lock(m_connection_state_lock);
323
324 if (m_state != session::state::open) {
325 ec = error::make_error_code(error::invalid_state);
326 return;
327 }
328
329 ec = this->send_close_frame(code,tr,false,close::status::terminal(code));
330}
331
332#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
333template<typename config>
334void connection<config>::close(close::status::value const code,
335 std::string const & reason)
336{
337 lib::error_code ec;
338 close(code,reason,ec);
339 if (ec) {
340 throw exception(ec);
341 }
342}
343#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
344
345/// Trigger the on_interrupt handler
346/**
347 * This is thread safe if the transport is thread safe
348 */
349template <typename config>
350lib::error_code connection<config>::interrupt() {
351 m_alog->write(log::alevel::devel,"connection connection::interrupt");
352 return transport_con_type::interrupt(
353 lib::bind(
356 )
357 );
358}
359
360
361template <typename config>
363 if (m_interrupt_handler) {
364 m_interrupt_handler(m_connection_hdl);
365 }
366}
367
368template <typename config>
369lib::error_code connection<config>::pause_reading() {
370 m_alog->write(log::alevel::devel,"connection connection::pause_reading");
371 return transport_con_type::dispatch(
372 lib::bind(
375 )
376 );
377}
378
379/// Pause reading handler. Not safe to call directly
380template <typename config>
382 m_alog->write(log::alevel::devel,"connection connection::handle_pause_reading");
383 m_read_flag = false;
384}
385
386template <typename config>
387lib::error_code connection<config>::resume_reading() {
388 m_alog->write(log::alevel::devel,"connection connection::resume_reading");
389 return transport_con_type::dispatch(
390 lib::bind(
393 )
394 );
395}
396
397/// Resume reading helper method. Not safe to call directly
398template <typename config>
400 m_read_flag = true;
402}
403
404
405
406
407
408
409
410
411
412
413
414template <typename config>
415bool connection<config>::get_secure() const {
416 //scoped_lock_type lock(m_connection_state_lock);
417 return m_uri->get_secure();
418}
419
420template <typename config>
421std::string const & connection<config>::get_host() const {
422 //scoped_lock_type lock(m_connection_state_lock);
423 return m_uri->get_host();
424}
425
426template <typename config>
427std::string const & connection<config>::get_resource() const {
428 //scoped_lock_type lock(m_connection_state_lock);
429 return m_uri->get_resource();
430}
431
432template <typename config>
433uint16_t connection<config>::get_port() const {
434 //scoped_lock_type lock(m_connection_state_lock);
435 return m_uri->get_port();
436}
437
438template <typename config>
439uri_ptr connection<config>::get_uri() const {
440 //scoped_lock_type lock(m_connection_state_lock);
441 return m_uri;
442}
443
444template <typename config>
445void connection<config>::set_uri(uri_ptr uri) {
446 //scoped_lock_type lock(m_connection_state_lock);
447 m_uri = uri;
448}
449
450
451
452
453
454
455template <typename config>
456std::string const & connection<config>::get_subprotocol() const {
457 return m_subprotocol;
458}
459
460template <typename config>
461std::vector<std::string> const &
463 return m_requested_subprotocols;
464}
465
466template <typename config>
467void connection<config>::add_subprotocol(std::string const & value,
468 lib::error_code & ec)
469{
470 if (m_is_server) {
471 ec = error::make_error_code(error::client_only);
472 return;
473 }
474
475 // If the value is empty or has a non-RFC2616 token character it is invalid.
476 if (value.empty() || std::find_if(value.begin(),value.end(),
477 http::is_not_token_char) != value.end())
478 {
479 ec = error::make_error_code(error::invalid_subprotocol);
480 return;
481 }
482
483 m_requested_subprotocols.push_back(value);
484}
485
486#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
487template <typename config>
488void connection<config>::add_subprotocol(std::string const & value) {
489 lib::error_code ec;
490 this->add_subprotocol(value,ec);
491 if (ec) {
492 throw exception(ec);
493 }
494}
495#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
496
497
498template <typename config>
499void connection<config>::select_subprotocol(std::string const & value,
500 lib::error_code & ec)
501{
502 if (!m_is_server) {
503 ec = error::make_error_code(error::server_only);
504 return;
505 }
506
507 if (value.empty()) {
508 ec = lib::error_code();
509 return;
510 }
511
512 std::vector<std::string>::iterator it;
513
514 it = std::find(m_requested_subprotocols.begin(),
515 m_requested_subprotocols.end(),
516 value);
517
518 if (it == m_requested_subprotocols.end()) {
519 ec = error::make_error_code(error::unrequested_subprotocol);
520 return;
521 }
522
523 m_subprotocol = value;
524 ec = lib::error_code();
525}
526
527#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
528template <typename config>
529void connection<config>::select_subprotocol(std::string const & value) {
530 lib::error_code ec;
531 this->select_subprotocol(value,ec);
532 if (ec) {
533 throw exception(ec);
534 }
535}
536#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
537
538
539template <typename config>
540std::string const &
541connection<config>::get_request_header(std::string const & key) const {
542 return m_request.get_header(key);
543}
544
545template <typename config>
546std::string const &
547connection<config>::get_request_body() const {
548 return m_request.get_body();
549}
550
551template <typename config>
552std::string const &
553connection<config>::get_response_header(std::string const & key) const {
554 return m_response.get_header(key);
555}
556
557template <typename config>
559 lib::error_code & ec)
560{
561 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
562 ec = error::make_error_code(error::invalid_state);
563 return;
564 }
565 ec = m_response.set_status(code);
566}
567
568#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
569template <typename config>
571{
572 lib::error_code ec;
573 this->set_status(code, ec);
574 if (ec) {
575 throw exception("Call to set_status from invalid state", ec);
576 }
577}
578#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
579
580template <typename config>
581void connection<config>::set_status(http::status_code::value code,
582 std::string const & msg, lib::error_code & ec)
583{
584 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
585 ec = error::make_error_code(error::invalid_state);
586 return;
587 }
588
589 ec = m_response.set_status(code,msg);
590}
591
592#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
593template <typename config>
594void connection<config>::set_status(http::status_code::value code,
595 std::string const & msg)
596{
597 lib::error_code ec;
598 this->set_status(code, msg, ec);
599 if (ec) {
600 throw exception("Call to set_status from invalid state", ec);
601 }
602}
603#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
604
605template <typename config>
606void connection<config>::set_body(std::string const & value,
607 lib::error_code & ec)
608{
609 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
610 ec = error::make_error_code(error::invalid_state);
611 return;
612 }
613
614 ec = m_response.set_body(value);
615}
616
617#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
618template <typename config>
619void connection<config>::set_body(std::string const & value) {
620 lib::error_code ec;
621 this->set_body(value, ec);
622 if (ec) {
623 throw exception("Call to set_body from invalid state", ec);
624 }
625}
626#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
627
628#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
629template <typename config>
630void connection<config>::set_body(std::string && value,
631 lib::error_code & ec)
632{
633 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
634 ec = error::make_error_code(error::invalid_state);
635 return;
636 }
637
638 ec = m_response.set_body(std::move(value));
639}
640
641#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
642template <typename config>
643void connection<config>::set_body(std::string && value) {
644 lib::error_code ec;
645 this->set_body(std::move(value), ec);
646 if (ec) {
647 throw exception("Call to set_body from invalid state", ec);
648 }
649}
650#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
651#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
652
653template <typename config>
654void connection<config>::append_header(std::string const & key,
655 std::string const & val, lib::error_code & ec)
656{
657 if (m_is_server) {
658 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
659 // we are setting response headers for an incoming server connection
660 ec = m_response.append_header(key, val);
661 } else {
662 ec = error::make_error_code(error::invalid_state);
663 }
664 } else {
665 if (m_internal_state == istate::USER_INIT) {
666 // we are setting initial headers for an outgoing client connection
667 ec = m_request.append_header(key, val);
668 } else {
669 ec = error::make_error_code(error::invalid_state);
670 }
671 }
672}
673
674#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
675template <typename config>
676void connection<config>::append_header(std::string const & key,
677 std::string const & val)
678{
679 lib::error_code ec;
680 this->append_header(key, val, ec);
681 if (ec) {
682 throw exception("Call to append_header from invalid state", ec);
683 }
684}
685#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
686
687template <typename config>
688void connection<config>::replace_header(std::string const & key,
689 std::string const & val, lib::error_code & ec)
690{
691 if (m_is_server) {
692 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
693 // we are setting response headers for an incoming server connection
694 ec = m_response.replace_header(key, val);
695 } else {
696 ec = error::make_error_code(error::invalid_state);
697 }
698 } else {
699 if (m_internal_state == istate::USER_INIT) {
700 // we are setting initial headers for an outgoing client connection
701 ec = m_request.replace_header(key, val);
702 } else {
703 ec = error::make_error_code(error::invalid_state);
704 }
705 }
706}
707
708#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
709template <typename config>
710void connection<config>::replace_header(std::string const & key,
711 std::string const & val)
712{
713 lib::error_code ec;
714 this->replace_header(key, val, ec);
715 if (ec) {
716 throw exception("Call to replace_header from invalid state", ec);
717 }
718}
719#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
720
721template <typename config>
722void connection<config>::remove_header(std::string const & key,
723 lib::error_code & ec)
724{
725 if (m_is_server) {
726 if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
727 // we are setting response headers for an incoming server connection
728 ec = m_response.remove_header(key);
729 } else {
730 ec = error::make_error_code(error::invalid_state);
731 }
732 } else {
733 if (m_internal_state == istate::USER_INIT) {
734 // we are setting initial headers for an outgoing client connection
735 ec = m_request.remove_header(key);
736 } else {
737 ec = error::make_error_code(error::invalid_state);
738 }
739 }
740}
741
742#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
743template <typename config>
744void connection<config>::remove_header(std::string const & key)
745{
746 lib::error_code ec;
747 this->remove_header(key, ec);
748 if (ec) {
749 throw exception("Call to remove_header from invalid state", ec);
750 }
751}
752#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
753
754/// Defer HTTP Response until later
755/**
756 * Used in the http handler to defer the HTTP response for this connection
757 * until later. Handshake timers will be canceled and the connection will be
758 * left open until `send_http_response` or an equivalent is called.
759 *
760 * Warning: deferred connections won't time out and as a result can tie up
761 * resources.
762 *
763 * @return A status code, zero on success, non-zero otherwise
764 */
765template <typename config>
766lib::error_code connection<config>::defer_http_response() {
767 // Cancel handshake timer, otherwise the connection will time out and we'll
768 // close the connection before the app has a chance to send a response.
769 if (m_handshake_timer) {
770 m_handshake_timer->cancel();
771 m_handshake_timer.reset();
772 }
773
774 // Do something to signal deferral
775 m_http_state = session::http_state::deferred;
776
777 return lib::error_code();
778}
779
780/// Send deferred HTTP Response (exception free)
781/**
782 * Sends an http response to an HTTP connection that was deferred. This will
783 * send a complete response including all headers, status line, and body
784 * text. The connection will be closed afterwards.
785 *
786 * @since 0.6.0
787 *
788 * @param ec A status code, zero on success, non-zero otherwise
789 */
790template <typename config>
791void connection<config>::send_http_response(lib::error_code & ec) {
792 {
793 scoped_lock_type lock(m_connection_state_lock);
794 if (m_http_state != session::http_state::deferred) {
795 ec = error::make_error_code(error::invalid_state);
796 return;
797 }
798
799 m_http_state = session::http_state::body_written;
800 }
801
802 this->write_http_response(lib::error_code());
803 ec = lib::error_code();
804}
805
806#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
807template <typename config>
809 lib::error_code ec;
810 this->send_http_response(ec);
811 if (ec) {
812 throw exception(ec);
813 }
814}
815#endif // _WEBSOCKETPP_NO_EXCEPTIONS_
816
817
818
819
820/******** logic thread ********/
821
822template <typename config>
823void connection<config>::start() {
824 m_alog->write(log::alevel::devel,"connection start");
825
826 if (m_internal_state != istate::USER_INIT) {
827 m_alog->write(log::alevel::devel,"Start called in invalid state");
828 this->terminate(error::make_error_code(error::invalid_state));
829 return;
830 }
831
832 m_internal_state = istate::TRANSPORT_INIT;
833
834 // Depending on how the transport implements init this function may return
835 // immediately and call handle_transport_init later or call
836 // handle_transport_init from this function.
837 transport_con_type::init(
838 lib::bind(
839 &type::handle_transport_init,
841 lib::placeholders::_1
842 )
843 );
844}
845
846template <typename config>
847void connection<config>::handle_transport_init(lib::error_code const & ec) {
848 m_alog->write(log::alevel::devel,"connection handle_transport_init");
849
850 lib::error_code ecm = ec;
851
852 if (m_internal_state != istate::TRANSPORT_INIT) {
853 m_alog->write(log::alevel::devel,
854 "handle_transport_init must be called from transport init state");
855 ecm = error::make_error_code(error::invalid_state);
856 }
857
858 if (ecm) {
859 std::stringstream s;
860 s << "handle_transport_init received error: "<< ecm.message();
861 m_elog->write(log::elevel::rerror,s.str());
862
863 this->terminate(ecm);
864 return;
865 }
866
867 // At this point the transport is ready to read and write bytes.
868 if (m_is_server) {
869 m_internal_state = istate::READ_HTTP_REQUEST;
870 this->read_handshake(1);
871 } else {
872 // We are a client. Set the processor to the version specified in the
873 // config file and send a handshake request.
874 m_internal_state = istate::WRITE_HTTP_REQUEST;
875 m_processor = get_processor(config::client_version);
876 this->send_http_request();
877 }
878}
879
880template <typename config>
881void connection<config>::read_handshake(size_t num_bytes) {
882 m_alog->write(log::alevel::devel,"connection read_handshake");
883
884 if (m_open_handshake_timeout_dur > 0) {
885 m_handshake_timer = transport_con_type::set_timer(
886 m_open_handshake_timeout_dur,
887 lib::bind(
888 &type::handle_open_handshake_timeout,
890 lib::placeholders::_1
891 )
892 );
893 }
894
895 transport_con_type::async_read_at_least(
896 num_bytes,
897 m_buf,
898 config::connection_read_buffer_size,
899 lib::bind(
900 &type::handle_read_handshake,
902 lib::placeholders::_1,
903 lib::placeholders::_2
904 )
905 );
906}
907
908// All exit paths for this function need to call write_http_response() or submit
909// a new read request with this function as the handler.
910template <typename config>
911void connection<config>::handle_read_handshake(lib::error_code const & ec,
912 size_t bytes_transferred)
913{
914 m_alog->write(log::alevel::devel,"connection handle_read_handshake");
915
916 lib::error_code ecm = ec;
917
918 if (!ecm) {
919 scoped_lock_type lock(m_connection_state_lock);
920
921 if (m_state == session::state::connecting) {
922 if (m_internal_state != istate::READ_HTTP_REQUEST) {
923 ecm = error::make_error_code(error::invalid_state);
924 }
925 } else if (m_state == session::state::closed) {
926 // The connection was canceled while the response was being sent,
927 // usually by the handshake timer. This is basically expected
928 // (though hopefully rare) and there is nothing we can do so ignore.
929 m_alog->write(log::alevel::devel,
930 "handle_read_handshake invoked after connection was closed");
931 return;
932 } else {
933 ecm = error::make_error_code(error::invalid_state);
934 }
935 }
936
937 if (ecm) {
938 if (ecm == transport::error::eof && m_state == session::state::closed) {
939 // we expect to get eof if the connection is closed already
940 m_alog->write(log::alevel::devel,
941 "got (expected) eof/state error from closed con");
942 return;
943 }
944
945 log_err(log::elevel::rerror,"handle_read_handshake",ecm);
946 this->terminate(ecm);
947 return;
948 }
949
950 // Boundaries checking. TODO: How much of this should be done?
951 if (bytes_transferred > config::connection_read_buffer_size) {
952 m_elog->write(log::elevel::fatal,"Fatal boundaries checking error.");
953 this->terminate(make_error_code(error::general));
954 return;
955 }
956
957 size_t bytes_processed = 0;
958 lib::error_code consume_ec;
959
960 bytes_processed = m_request.consume(m_buf, bytes_transferred, consume_ec);
961 if (consume_ec) {
962 // All HTTP errors will result in this request failing and an error
963 // response being returned. No more bytes will be read in this con.
964 m_response.set_status(http::error::get_status_code(http::error::value(consume_ec.value())));
965 log_err(log::elevel::fatal,"Fatal error reading request: ",consume_ec);
966 this->write_http_response_error(error::make_error_code(error::http_parse_error));
967 return;
968 }
969
970 // More paranoid boundaries checking.
971 // TODO: Is this overkill?
972 if (bytes_processed > bytes_transferred) {
973 m_elog->write(log::elevel::fatal,"Fatal boundaries checking error.");
974 this->terminate(make_error_code(error::general));
975 return;
976 }
977
978 if (m_alog->static_test(log::alevel::devel)) {
979 std::stringstream s;
980 s << "bytes_transferred: " << bytes_transferred
981 << " bytes, bytes processed: " << bytes_processed << " bytes";
982 m_alog->write(log::alevel::devel,s.str());
983 }
984
985 if (m_request.ready()) {
986 lib::error_code processor_ec = this->initialize_processor();
987 if (processor_ec) {
988 this->write_http_response_error(processor_ec);
989 return;
990 }
991
992 if (m_processor && m_processor->get_version() == 0) {
993 // Version 00 has an extra requirement to read some bytes after the
994 // handshake
995 if (bytes_transferred-bytes_processed >= 8) {
996 m_request.replace_header(
997 "Sec-WebSocket-Key3",
998 std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
999 );
1000 bytes_processed += 8;
1001 } else {
1002 // TODO: need more bytes
1003 m_alog->write(log::alevel::devel,"short key3 read");
1004 m_response.set_status(http::status_code::internal_server_error);
1006 return;
1007 }
1008 }
1009
1010 if (m_alog->static_test(log::alevel::devel)) {
1011 m_alog->write(log::alevel::devel,m_request.raw());
1012 if (!m_request.get_header("Sec-WebSocket-Key3").empty()) {
1013 m_alog->write(log::alevel::devel,
1014 utility::to_hex(m_request.get_header("Sec-WebSocket-Key3")));
1015 }
1016 }
1017
1018 // The remaining bytes in m_buf are frame data. Copy them to the
1019 // beginning of the buffer and note the length. They will be read after
1020 // the handshake completes and before more bytes are read.
1021 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1022 m_buf_cursor = bytes_transferred-bytes_processed;
1023
1024
1025 m_internal_state = istate::PROCESS_HTTP_REQUEST;
1026
1027 // We have the complete request. Process it.
1028 lib::error_code handshake_ec = this->process_handshake_request();
1029
1030 // Write a response if this is a websocket connection or if it is an
1031 // HTTP connection for which the response has not been deferred or
1032 // started yet by a different system (i.e. still in init state).
1033 if (!m_is_http || m_http_state == session::http_state::init) {
1034 this->write_http_response(handshake_ec);
1035 }
1036 } else {
1037 // The HTTP parser reported that it was not ready and wants more data.
1038 // Assert that it actually consumed all the data present before overwriting
1039 // the buffer. This should always be the case.
1040 if (bytes_transferred != bytes_processed) {
1041 m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP request parser failed to consume all bytes from a read request.");
1042 this->terminate(make_error_code(error::general));
1043 return;
1044 }
1045
1046 // read at least 1 more byte
1047 transport_con_type::async_read_at_least(
1048 1,
1049 m_buf,
1050 config::connection_read_buffer_size,
1051 lib::bind(
1052 &type::handle_read_handshake,
1054 lib::placeholders::_1,
1055 lib::placeholders::_2
1056 )
1057 );
1058 }
1059}
1060
1061// write_http_response requires the request to be fully read and the connection
1062// to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors
1063// before the request is fully read (specifically at a point where we aren't
1064// sure if the hybi00 key3 bytes need to be read). This method sets the correct
1065// state and calls write_http_response
1066template <typename config>
1067void connection<config>::write_http_response_error(lib::error_code const & ec) {
1068 if (m_internal_state != istate::READ_HTTP_REQUEST) {
1069 m_alog->write(log::alevel::devel,
1070 "write_http_response_error called in invalid state");
1071 this->terminate(error::make_error_code(error::invalid_state));
1072 return;
1073 }
1074
1075 m_internal_state = istate::PROCESS_HTTP_REQUEST;
1076
1077 this->write_http_response(ec);
1078}
1079
1080// All exit paths for this function need to call write_http_response() or submit
1081// a new read request with this function as the handler.
1082template <typename config>
1083void connection<config>::handle_read_frame(lib::error_code const & ec,
1084 size_t bytes_transferred)
1085{
1086 //m_alog->write(log::alevel::devel,"connection handle_read_frame");
1087
1088 lib::error_code ecm = ec;
1089
1090 if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
1091 ecm = error::make_error_code(error::invalid_state);
1092 }
1093
1094 if (ecm) {
1095 log::level echannel = log::elevel::rerror;
1096
1097 if (ecm == transport::error::eof) {
1098 if (m_state == session::state::closed) {
1099 // we expect to get eof if the connection is closed already
1100 // just ignore it
1101 m_alog->write(log::alevel::devel,"got eof from closed con");
1102 return;
1103 } else if (m_state == session::state::closing && !m_is_server) {
1104 // If we are a client we expect to get eof in the closing state,
1105 // this is a signal to terminate our end of the connection after
1106 // the closing handshake
1107 terminate(lib::error_code());
1108 return;
1109 }
1110 } else if (ecm == error::invalid_state) {
1111 // In general, invalid state errors in the closed state are the
1112 // result of handlers that were in the system already when the state
1113 // changed and should be ignored as they pose no problems and there
1114 // is nothing useful that we can do about them.
1115 if (m_state == session::state::closed) {
1116 m_alog->write(log::alevel::devel,
1117 "handle_read_frame: got invalid istate in closed state");
1118 return;
1119 }
1120 } else if (ecm == transport::error::action_after_shutdown) {
1121 echannel = log::elevel::info;
1122 } else {
1123 // TODO: more generally should we do something different here in the
1124 // case that m_state is cosed? Are errors after the connection is
1125 // already closed really an rerror?
1126 }
1127
1128
1129
1130 log_err(echannel, "handle_read_frame", ecm);
1131 this->terminate(ecm);
1132 return;
1133 }
1134
1135 // Boundaries checking. TODO: How much of this should be done?
1136 /*if (bytes_transferred > config::connection_read_buffer_size) {
1137 m_elog->write(log::elevel::fatal,"Fatal boundaries checking error");
1138 this->terminate(make_error_code(error::general));
1139 return;
1140 }*/
1141
1142 size_t p = 0;
1143
1144 if (m_alog->static_test(log::alevel::devel)) {
1145 std::stringstream s;
1146 s << "p = " << p << " bytes transferred = " << bytes_transferred;
1147 m_alog->write(log::alevel::devel,s.str());
1148 }
1149
1150 while (p < bytes_transferred) {
1151 if (m_alog->static_test(log::alevel::devel)) {
1152 std::stringstream s;
1153 s << "calling consume with " << bytes_transferred-p << " bytes";
1154 m_alog->write(log::alevel::devel,s.str());
1155 }
1156
1157 lib::error_code consume_ec;
1158
1159 if (m_alog->static_test(log::alevel::devel)) {
1160 std::stringstream s;
1161 s << "Processing Bytes: " << utility::to_hex(reinterpret_cast<uint8_t*>(m_buf)+p,bytes_transferred-p);
1162 m_alog->write(log::alevel::devel,s.str());
1163 }
1164
1165 p += m_processor->consume(
1166 reinterpret_cast<uint8_t*>(m_buf)+p,
1167 bytes_transferred-p,
1168 consume_ec
1169 );
1170
1171 if (m_alog->static_test(log::alevel::devel)) {
1172 std::stringstream s;
1173 s << "bytes left after consume: " << bytes_transferred-p;
1174 m_alog->write(log::alevel::devel,s.str());
1175 }
1176 if (consume_ec) {
1177 log_err(log::elevel::rerror, "consume", consume_ec);
1178
1179 if (config::drop_on_protocol_error) {
1180 this->terminate(consume_ec);
1181 return;
1182 } else {
1183 lib::error_code close_ec;
1184 this->close(
1185 processor::error::to_ws(consume_ec),
1186 consume_ec.message(),
1187 close_ec
1188 );
1189
1190 if (close_ec) {
1191 log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
1192 this->terminate(close_ec);
1193 return;
1194 }
1195 }
1196 return;
1197 }
1198
1199 if (m_processor->ready()) {
1200 if (m_alog->static_test(log::alevel::devel)) {
1201 std::stringstream s;
1202 s << "Complete message received. Dispatching";
1203 m_alog->write(log::alevel::devel,s.str());
1204 }
1205
1206 message_ptr msg = m_processor->get_message();
1207
1208 if (!msg) {
1209 m_alog->write(log::alevel::devel, "null message from m_processor");
1210 } else if (!is_control(msg->get_opcode())) {
1211 // data message, dispatch to user
1212 if (m_state != session::state::open) {
1213 m_elog->write(log::elevel::warn, "got non-close frame while closing");
1214 } else if (m_message_handler) {
1215 m_message_handler(m_connection_hdl, msg);
1216 }
1217 } else {
1218 process_control_frame(msg);
1219 }
1220 }
1221 }
1222
1224}
1225
1226/// Issue a new transport read unless reading is paused.
1227template <typename config>
1228void connection<config>::read_frame() {
1229 if (!m_read_flag) {
1230 return;
1231 }
1232
1233 transport_con_type::async_read_at_least(
1234 // std::min wont work with undefined static const values.
1235 // TODO: is there a more elegant way to do this?
1236 // Need to determine if requesting 1 byte or the exact number of bytes
1237 // is better here. 1 byte lets us be a bit more responsive at a
1238 // potential expense of additional runs through handle_read_frame
1239 /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
1240 config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
1241 1,
1242 m_buf,
1243 config::connection_read_buffer_size,
1244 m_handle_read_frame
1245 );
1246}
1247
1248template <typename config>
1249lib::error_code connection<config>::initialize_processor() {
1250 m_alog->write(log::alevel::devel,"initialize_processor");
1251
1252 // if it isn't a websocket handshake nothing to do.
1253 if (!processor::is_websocket_handshake(m_request)) {
1254 return lib::error_code();
1255 }
1256
1257 int version = processor::get_websocket_version(m_request);
1258
1259 if (version < 0) {
1260 m_alog->write(log::alevel::devel, "BAD REQUEST: can't determine version");
1261 m_response.set_status(http::status_code::bad_request);
1262 return error::make_error_code(error::invalid_version);
1263 }
1264
1265 m_processor = get_processor(version);
1266
1267 // if the processor is not null we are done
1268 if (m_processor) {
1269 return lib::error_code();
1270 }
1271
1272 // We don't have a processor for this version. Return bad request
1273 // with Sec-WebSocket-Version header filled with values we do accept
1274 m_alog->write(log::alevel::devel, "BAD REQUEST: no processor for version");
1275 m_response.set_status(http::status_code::bad_request);
1276
1277 std::stringstream ss;
1278 std::string sep;
1279 std::vector<int>::const_iterator it;
1280 for (it = versions_supported.begin(); it != versions_supported.end(); it++)
1281 {
1282 ss << sep << *it;
1283 sep = ",";
1284 }
1285
1286 m_response.replace_header("Sec-WebSocket-Version",ss.str());
1287 return error::make_error_code(error::unsupported_version);
1288}
1289
1290template <typename config>
1291lib::error_code connection<config>::process_handshake_request() {
1292 m_alog->write(log::alevel::devel,"process handshake request");
1293
1294 if (!processor::is_websocket_handshake(m_request)) {
1295 // this is not a websocket handshake. Process as plain HTTP
1296 m_alog->write(log::alevel::devel,"HTTP REQUEST");
1297
1298 // extract URI from request
1299 m_uri = processor::get_uri_from_host(
1300 m_request,
1301 (transport_con_type::is_secure() ? "https" : "http")
1302 );
1303
1304 if (!m_uri->get_valid()) {
1305 m_alog->write(log::alevel::devel, "Bad request: failed to parse uri");
1306 m_response.set_status(http::status_code::bad_request);
1307 return error::make_error_code(error::invalid_uri);
1308 }
1309
1310 if (m_http_handler) {
1311 m_is_http = true;
1312 m_http_handler(m_connection_hdl);
1313
1314 if (m_state == session::state::closed) {
1315 return error::make_error_code(error::http_connection_ended);
1316 }
1317 } else {
1318 m_response.set_status(http::status_code::upgrade_required);
1319 return error::make_error_code(error::upgrade_required);
1320 }
1321
1322 return lib::error_code();
1323 }
1324
1325 lib::error_code ec = m_processor->validate_handshake(m_request);
1326
1327 // Validate: make sure all required elements are present.
1328 if (ec){
1329 // Not a valid handshake request
1330 m_alog->write(log::alevel::devel, "Bad request " + ec.message());
1331 m_response.set_status(http::status_code::bad_request);
1332 return ec;
1333 }
1334
1335 // Read extension parameters and set up values necessary for the end user
1336 // to complete extension negotiation.
1337 std::pair<lib::error_code,std::string> neg_results;
1338 neg_results = m_processor->negotiate_extensions(m_request);
1339
1341 // There was a fatal error in extension parsing that should result in
1342 // a failed connection attempt.
1343 m_elog->write(log::elevel::info, "Bad request: " + neg_results.first.message());
1344 m_response.set_status(http::status_code::bad_request);
1345 return neg_results.first;
1346 } else if (neg_results.first) {
1347 // There was a fatal error in extension processing that is probably our
1348 // fault. Consider extension negotiation to have failed and continue as
1349 // if extensions were not supported
1350 m_elog->write(log::elevel::info,
1351 "Extension negotiation failed: " + neg_results.first.message());
1352 } else {
1353 // extension negotiation succeeded, set response header accordingly
1354 // we don't send an empty extensions header because it breaks many
1355 // clients.
1356 if (neg_results.second.size() > 0) {
1357 m_response.replace_header("Sec-WebSocket-Extensions",
1358 neg_results.second);
1359 }
1360 }
1361
1362 // extract URI from request
1363 m_uri = m_processor->get_uri(m_request);
1364
1365
1366 if (!m_uri->get_valid()) {
1367 m_alog->write(log::alevel::devel, "Bad request: failed to parse uri");
1368 m_response.set_status(http::status_code::bad_request);
1369 return error::make_error_code(error::invalid_uri);
1370 }
1371
1372 // extract subprotocols
1373 lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
1374 m_requested_subprotocols);
1375
1376 if (subp_ec) {
1377 // should we do anything?
1378 }
1379
1380 // Ask application to validate the connection
1381 if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
1382 m_response.set_status(http::status_code::switching_protocols);
1383
1384 // Write the appropriate response headers based on request and
1385 // processor version
1386 ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
1387
1388 if (ec) {
1389 std::stringstream s;
1390 s << "Processing error: " << ec << "(" << ec.message() << ")";
1391 m_alog->write(log::alevel::devel, s.str());
1392
1393 m_response.set_status(http::status_code::internal_server_error);
1394 return ec;
1395 }
1396 } else {
1397 // User application has rejected the handshake
1398 m_alog->write(log::alevel::devel, "USER REJECT");
1399
1400 // Use Bad Request if the user handler did not provide a more
1401 // specific http response error code.
1402 // TODO: is there a better default?
1403 if (m_response.get_status_code() == http::status_code::uninitialized) {
1404 m_response.set_status(http::status_code::bad_request);
1405 }
1406
1407 return error::make_error_code(error::rejected);
1408 }
1409
1410 return lib::error_code();
1411}
1412
1413template <typename config>
1414void connection<config>::write_http_response(lib::error_code const & ec) {
1415 m_alog->write(log::alevel::devel,"connection write_http_response");
1416
1417 if (ec == error::make_error_code(error::http_connection_ended)) {
1418 m_alog->write(log::alevel::http,"An HTTP handler took over the connection.");
1419 return;
1420 }
1421
1422 if (m_response.get_status_code() == http::status_code::uninitialized) {
1423 lib::error_code status_ec;
1424 m_response.set_status(http::status_code::internal_server_error);
1425 m_ec = error::make_error_code(error::general);
1426 } else {
1427 m_ec = ec;
1428 }
1429
1430 m_response.set_version("HTTP/1.1");
1431
1432 // Set server header based on the user agent settings
1433 if (m_response.get_header("Server").empty()) {
1434 if (!m_user_agent.empty()) {
1435 m_response.replace_header("Server",m_user_agent);
1436 } else {
1437 m_response.remove_header("Server");
1438 }
1439 }
1440
1441 // have the processor generate the raw bytes for the wire (if it exists)
1442 if (m_processor) {
1443 m_handshake_buffer = m_processor->get_raw(m_response);
1444 } else {
1445 // a processor wont exist for raw HTTP responses.
1446 m_handshake_buffer = m_response.raw();
1447 }
1448
1449 if (m_alog->static_test(log::alevel::devel)) {
1450 m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
1451 if (!m_response.get_header("Sec-WebSocket-Key3").empty()) {
1452 m_alog->write(log::alevel::devel,
1453 utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
1454 }
1455 }
1456
1457 // write raw bytes
1458 transport_con_type::async_write(
1459 m_handshake_buffer.data(),
1460 m_handshake_buffer.size(),
1461 lib::bind(
1462 &type::handle_write_http_response,
1464 lib::placeholders::_1
1465 )
1466 );
1467}
1468
1469template <typename config>
1470void connection<config>::handle_write_http_response(lib::error_code const & ec) {
1471 m_alog->write(log::alevel::devel,"handle_write_http_response");
1472
1473 lib::error_code ecm = ec;
1474
1475 if (!ecm) {
1476 scoped_lock_type lock(m_connection_state_lock);
1477
1478 if (m_state == session::state::connecting) {
1479 if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
1480 ecm = error::make_error_code(error::invalid_state);
1481 }
1482 } else if (m_state == session::state::closed) {
1483 // The connection was canceled while the response was being sent,
1484 // usually by the handshake timer. This is basically expected
1485 // (though hopefully rare) and there is nothing we can do so ignore.
1486 m_alog->write(log::alevel::devel,
1487 "handle_write_http_response invoked after connection was closed");
1488 return;
1489 } else {
1490 ecm = error::make_error_code(error::invalid_state);
1491 }
1492 }
1493
1494 if (ecm) {
1495 if (ecm == transport::error::eof && m_state == session::state::closed) {
1496 // we expect to get eof if the connection is closed already
1497 m_alog->write(log::alevel::devel,
1498 "got (expected) eof/state error from closed con");
1499 return;
1500 }
1501
1502 log_err(log::elevel::rerror,"handle_write_http_response",ecm);
1503 this->terminate(ecm);
1504 return;
1505 }
1506
1507 if (m_handshake_timer) {
1508 m_handshake_timer->cancel();
1509 m_handshake_timer.reset();
1510 }
1511
1512 if (m_response.get_status_code() != http::status_code::switching_protocols)
1513 {
1514 /*if (m_processor || m_ec == error::http_parse_error ||
1515 m_ec == error::invalid_version || m_ec == error::unsupported_version
1516 || m_ec == error::upgrade_required)
1517 {*/
1518 if (!m_is_http) {
1519 std::stringstream s;
1520 s << "Handshake ended with HTTP error: "
1521 << m_response.get_status_code();
1522 m_elog->write(log::elevel::rerror,s.str());
1523 } else {
1524 // if this was not a websocket connection, we have written
1525 // the expected response and the connection can be closed.
1526
1527 this->log_http_result();
1528
1529 if (m_ec) {
1530 m_alog->write(log::alevel::devel,
1531 "got to writing HTTP results with m_ec set: "+m_ec.message());
1532 }
1533 m_ec = make_error_code(error::http_connection_ended);
1534 }
1535
1536 this->terminate(m_ec);
1537 return;
1538 }
1539
1540 this->log_open_result();
1541
1542 m_internal_state = istate::PROCESS_CONNECTION;
1543 m_state = session::state::open;
1544
1545 if (m_open_handler) {
1546 m_open_handler(m_connection_hdl);
1547 }
1548
1549 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1550}
1551
1552template <typename config>
1553void connection<config>::send_http_request() {
1554 m_alog->write(log::alevel::devel,"connection send_http_request");
1555
1556 // TODO: origin header?
1557
1558 // Have the protocol processor fill in the appropriate fields based on the
1559 // selected client version
1560 if (m_processor) {
1561 lib::error_code ec;
1562 ec = m_processor->client_handshake_request(m_request,m_uri,
1563 m_requested_subprotocols);
1564
1565 if (ec) {
1566 log_err(log::elevel::fatal,"Internal library error: Processor",ec);
1567 return;
1568 }
1569 } else {
1570 m_elog->write(log::elevel::fatal,"Internal library error: missing processor");
1571 return;
1572 }
1573
1574 // Unless the user has overridden the user agent, send generic WS++ UA.
1575 if (m_request.get_header("User-Agent").empty()) {
1576 if (!m_user_agent.empty()) {
1577 m_request.replace_header("User-Agent",m_user_agent);
1578 } else {
1579 m_request.remove_header("User-Agent");
1580 }
1581 }
1582
1583 m_handshake_buffer = m_request.raw();
1584
1585 if (m_alog->static_test(log::alevel::devel)) {
1586 m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
1587 }
1588
1589 if (m_open_handshake_timeout_dur > 0) {
1590 m_handshake_timer = transport_con_type::set_timer(
1591 m_open_handshake_timeout_dur,
1592 lib::bind(
1593 &type::handle_open_handshake_timeout,
1595 lib::placeholders::_1
1596 )
1597 );
1598 }
1599
1600 transport_con_type::async_write(
1601 m_handshake_buffer.data(),
1602 m_handshake_buffer.size(),
1603 lib::bind(
1604 &type::handle_send_http_request,
1606 lib::placeholders::_1
1607 )
1608 );
1609}
1610
1611template <typename config>
1612void connection<config>::handle_send_http_request(lib::error_code const & ec) {
1613 m_alog->write(log::alevel::devel,"handle_send_http_request");
1614
1615 lib::error_code ecm = ec;
1616
1617 if (!ecm) {
1618 scoped_lock_type lock(m_connection_state_lock);
1619
1620 if (m_state == session::state::connecting) {
1621 if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
1622 ecm = error::make_error_code(error::invalid_state);
1623 } else {
1624 m_internal_state = istate::READ_HTTP_RESPONSE;
1625 }
1626 } else if (m_state == session::state::closed) {
1627 // The connection was canceled while the response was being sent,
1628 // usually by the handshake timer. This is basically expected
1629 // (though hopefully rare) and there is nothing we can do so ignore.
1630 m_alog->write(log::alevel::devel,
1631 "handle_send_http_request invoked after connection was closed");
1632 return;
1633 } else {
1634 ecm = error::make_error_code(error::invalid_state);
1635 }
1636 }
1637
1638 if (ecm) {
1639 if (ecm == transport::error::eof && m_state == session::state::closed) {
1640 // we expect to get eof if the connection is closed already
1641 m_alog->write(log::alevel::devel,
1642 "got (expected) eof/state error from closed con");
1643 return;
1644 }
1645
1646 log_err(log::elevel::rerror,"handle_send_http_request",ecm);
1647 this->terminate(ecm);
1648 return;
1649 }
1650
1651 transport_con_type::async_read_at_least(
1652 1,
1653 m_buf,
1654 config::connection_read_buffer_size,
1655 lib::bind(
1656 &type::handle_read_http_response,
1658 lib::placeholders::_1,
1659 lib::placeholders::_2
1660 )
1661 );
1662}
1663
1664template <typename config>
1665void connection<config>::handle_read_http_response(lib::error_code const & ec,
1666 size_t bytes_transferred)
1667{
1668 m_alog->write(log::alevel::devel,"handle_read_http_response");
1669
1670 lib::error_code ecm = ec;
1671
1672 if (!ecm) {
1673 scoped_lock_type lock(m_connection_state_lock);
1674
1675 if (m_state == session::state::connecting) {
1676 if (m_internal_state != istate::READ_HTTP_RESPONSE) {
1677 ecm = error::make_error_code(error::invalid_state);
1678 }
1679 } else if (m_state == session::state::closed) {
1680 // The connection was canceled while the response was being sent,
1681 // usually by the handshake timer. This is basically expected
1682 // (though hopefully rare) and there is nothing we can do so ignore.
1683 m_alog->write(log::alevel::devel,
1684 "handle_read_http_response invoked after connection was closed");
1685 return;
1686 } else {
1687 ecm = error::make_error_code(error::invalid_state);
1688 }
1689 }
1690
1691 if (ecm) {
1692 if (ecm == transport::error::eof && m_state == session::state::closed) {
1693 // we expect to get eof if the connection is closed already
1694 m_alog->write(log::alevel::devel,
1695 "got (expected) eof/state error from closed con");
1696 return;
1697 }
1698
1699 log_err(log::elevel::rerror,"handle_read_http_response",ecm);
1700 this->terminate(ecm);
1701 return;
1702 }
1703
1704 size_t bytes_processed = 0;
1705
1706 lib::error_code consume_ec;
1707
1708 bytes_processed = m_response.consume(m_buf, bytes_transferred, consume_ec);
1709 if (consume_ec) {
1710 // An HTTP error while reading a response doesn't give us many options other than log
1711 // and terminate.
1712 m_response.set_status(http::error::get_status_code(http::error::value(consume_ec.value())));
1713 log_err(log::elevel::rerror,"error in handle_read_http_response: ",consume_ec);
1714 this->terminate(make_error_code(error::general));
1715 return;
1716 }
1717
1718 m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
1719
1720 if (m_response.headers_ready()) {
1721 if (m_handshake_timer) {
1722 m_handshake_timer->cancel();
1723 m_handshake_timer.reset();
1724 }
1725
1726 lib::error_code validate_ec = m_processor->validate_server_handshake_response(
1727 m_request,
1728 m_response
1729 );
1730 if (validate_ec) {
1731 log_err(log::elevel::rerror,"Server handshake response",validate_ec);
1732 this->terminate(validate_ec);
1733 return;
1734 }
1735
1736 // Read extension parameters and set up values necessary for the end
1737 // user to complete extension negotiation.
1738 std::pair<lib::error_code,std::string> neg_results;
1739 neg_results = m_processor->negotiate_extensions(m_response);
1740
1741 if (neg_results.first) {
1742 // There was a fatal error in extension negotiation. For the moment
1743 // kill all connections that fail extension negotiation.
1744
1745 // TODO: deal with cases where the response is well formed but
1746 // doesn't match the options requested by the client. Its possible
1747 // that the best behavior in this cases is to log and continue with
1748 // an unextended connection.
1749 m_alog->write(log::alevel::devel, "Extension negotiation failed: "
1750 + neg_results.first.message());
1751 this->terminate(make_error_code(error::extension_neg_failed));
1752 // TODO: close connection with reason 1010 (and list extensions)
1753 }
1754
1755 // response is valid, connection can now be assumed to be open
1756 m_internal_state = istate::PROCESS_CONNECTION;
1757 m_state = session::state::open;
1758
1759 this->log_open_result();
1760
1761 if (m_open_handler) {
1762 m_open_handler(m_connection_hdl);
1763 }
1764
1765 // The remaining bytes in m_buf are frame data. Copy them to the
1766 // beginning of the buffer and note the length. They will be read after
1767 // the handshake completes and before more bytes are read.
1768 std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
1769 m_buf_cursor = bytes_transferred-bytes_processed;
1770
1771 this->handle_read_frame(lib::error_code(), m_buf_cursor);
1772 } else {
1773 // The HTTP parser reported that it was not ready and wants more data.
1774 // Assert that it actually consumed all the data present before overwriting
1775 // the buffer. This should always be the case.
1776 if (bytes_transferred != bytes_processed) {
1777 m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP response parser failed to consume all bytes from a read request.");
1778 this->terminate(make_error_code(error::general));
1779 return;
1780 }
1781
1782 transport_con_type::async_read_at_least(
1783 1,
1784 m_buf,
1785 config::connection_read_buffer_size,
1786 lib::bind(
1787 &type::handle_read_http_response,
1789 lib::placeholders::_1,
1790 lib::placeholders::_2
1791 )
1792 );
1793 }
1794}
1795
1796template <typename config>
1797void connection<config>::handle_open_handshake_timeout(
1798 lib::error_code const & ec)
1799{
1801 m_alog->write(log::alevel::devel,"open handshake timer cancelled");
1802 } else if (ec) {
1803 m_alog->write(log::alevel::devel,
1804 "open handle_open_handshake_timeout error: "+ec.message());
1805 // TODO: ignore or fail here?
1806 } else {
1807 m_alog->write(log::alevel::devel,"open handshake timer expired");
1808 terminate(make_error_code(error::open_handshake_timeout));
1809 }
1810}
1811
1812template <typename config>
1813void connection<config>::handle_close_handshake_timeout(
1814 lib::error_code const & ec)
1815{
1817 m_alog->write(log::alevel::devel,"asio close handshake timer cancelled");
1818 } else if (ec) {
1819 m_alog->write(log::alevel::devel,
1820 "asio open handle_close_handshake_timeout error: "+ec.message());
1821 // TODO: ignore or fail here?
1822 } else {
1823 m_alog->write(log::alevel::devel, "asio close handshake timer expired");
1824 terminate(make_error_code(error::close_handshake_timeout));
1825 }
1826}
1827
1828template <typename config>
1829void connection<config>::terminate(lib::error_code const & ec) {
1830 if (m_alog->static_test(log::alevel::devel)) {
1831 m_alog->write(log::alevel::devel,"connection terminate");
1832 }
1833
1834 // Cancel close handshake timer
1835 if (m_handshake_timer) {
1836 m_handshake_timer->cancel();
1837 m_handshake_timer.reset();
1838 }
1839
1840 // Cancel ping timer
1841 if (m_ping_timer) {
1842 m_ping_timer->cancel();
1843 m_handshake_timer.reset();
1844 }
1845
1846 terminate_status tstat = unknown;
1847 if (ec) {
1848 m_ec = ec;
1849 m_local_close_code = close::status::abnormal_close;
1850 m_local_close_reason = ec.message();
1851 }
1852
1853 // TODO: does any of this need a mutex?
1854 if (m_is_http) {
1855 m_http_state = session::http_state::closed;
1856 }
1857 if (m_state == session::state::connecting) {
1858 m_state = session::state::closed;
1859 tstat = failed;
1860
1861 // Log fail result here before socket is shut down and we can't get
1862 // the remote address, etc anymore
1863 if (m_ec != error::http_connection_ended) {
1864 log_fail_result();
1865 }
1866 } else if (m_state != session::state::closed) {
1867 m_state = session::state::closed;
1868 tstat = closed;
1869 } else {
1870 m_alog->write(log::alevel::devel,
1871 "terminate called on connection that was already terminated");
1872 return;
1873 }
1874
1875 // TODO: choose between shutdown and close based on error code sent
1876
1877 transport_con_type::async_shutdown(
1878 lib::bind(
1879 &type::handle_terminate,
1881 tstat,
1882 lib::placeholders::_1
1883 )
1884 );
1885}
1886
1887template <typename config>
1888void connection<config>::handle_terminate(terminate_status tstat,
1889 lib::error_code const & ec)
1890{
1891 if (m_alog->static_test(log::alevel::devel)) {
1892 m_alog->write(log::alevel::devel,"connection handle_terminate");
1893 }
1894
1895 if (ec) {
1896 // there was an error actually shutting down the connection
1897 log_err(log::elevel::devel,"handle_terminate",ec);
1898 }
1899
1900 // clean shutdown
1901 if (tstat == failed) {
1902 if (m_ec != error::http_connection_ended) {
1903 if (m_fail_handler) {
1904 m_fail_handler(m_connection_hdl);
1905 }
1906 }
1907 } else if (tstat == closed) {
1908 if (m_close_handler) {
1909 m_close_handler(m_connection_hdl);
1910 }
1911 log_close_result();
1912 } else {
1913 m_elog->write(log::elevel::rerror,"Unknown terminate_status");
1914 }
1915
1916 // call the termination handler if it exists
1917 // if it exists it might (but shouldn't) refer to a bad memory location.
1918 // If it does, we don't care and should catch and ignore it.
1919 // todo: there has to be a better way to do this.
1920 if (m_termination_handler) {
1921#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_
1922 try {
1923 m_termination_handler(type::get_shared());
1924 } catch (std::exception const & e) {
1925 m_elog->write(log::elevel::warn,
1926 std::string("termination_handler call failed. Reason was: ")+e.what());
1927 }
1928#else
1929 m_termination_handler(type::get_shared());
1930#endif
1931 }
1932}
1933
1934template <typename config>
1935void connection<config>::write_frame() {
1936 //m_alog->write(log::alevel::devel,"connection write_frame");
1937
1938 {
1939 scoped_lock_type lock(m_write_lock);
1940
1941 // Check the write flag. If true, there is an outstanding transport
1942 // write already. In this case we just return. The write handler will
1943 // start a new write if the write queue isn't empty. If false, we set
1944 // the write flag and proceed to initiate a transport write.
1945 if (m_write_flag) {
1946 return;
1947 }
1948
1949 // pull off all the messages that are ready to write.
1950 // stop if we get a message marked terminal
1951 message_ptr next_message = write_pop();
1952 while (next_message) {
1953 m_current_msgs.push_back(next_message);
1954 if (!next_message->get_terminal()) {
1955 next_message = write_pop();
1956 } else {
1957 next_message = message_ptr();
1958 }
1959 }
1960
1961 if (m_current_msgs.empty()) {
1962 // there was nothing to send
1963 return;
1964 } else {
1965 // At this point we own the next messages to be sent and are
1966 // responsible for holding the write flag until they are
1967 // successfully sent or there is some error
1968 m_write_flag = true;
1969 }
1970 }
1971
1972 typename std::vector<message_ptr>::iterator it;
1973 for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
1974 std::string const & header = (*it)->get_header();
1975 std::string const & payload = (*it)->get_payload();
1976
1977 m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
1978 m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
1979 }
1980
1981 // Print detailed send stats if those log levels are enabled
1982 if (m_alog->static_test(log::alevel::frame_header)) {
1983 if (m_alog->dynamic_test(log::alevel::frame_header)) {
1984 std::stringstream general,header,payload;
1985
1986 general << "Dispatching write containing " << m_current_msgs.size()
1987 <<" message(s) containing ";
1988 header << "Header Bytes: \n";
1989 payload << "Payload Bytes: \n";
1990
1991 size_t hbytes = 0;
1992 size_t pbytes = 0;
1993
1994 for (size_t i = 0; i < m_current_msgs.size(); i++) {
1995 hbytes += m_current_msgs[i]->get_header().size();
1996 pbytes += m_current_msgs[i]->get_payload().size();
1997
1998
1999 header << "[" << i << "] ("
2000 << m_current_msgs[i]->get_header().size() << ") "
2001 << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
2002
2003 if (m_alog->static_test(log::alevel::frame_payload)) {
2004 if (m_alog->dynamic_test(log::alevel::frame_payload)) {
2005 payload << "[" << i << "] ("
2006 << m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
2007 << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
2008 m_current_msgs[i]->get_payload() :
2009 utility::to_hex(m_current_msgs[i]->get_payload())
2010 )
2011 << "\n";
2012 }
2013 }
2014 }
2015
2016 general << hbytes << " header bytes and " << pbytes << " payload bytes";
2017
2018 m_alog->write(log::alevel::frame_header,general.str());
2019 m_alog->write(log::alevel::frame_header,header.str());
2020 m_alog->write(log::alevel::frame_payload,payload.str());
2021 }
2022 }
2023
2024 transport_con_type::async_write(
2025 m_send_buffer,
2026 m_write_frame_handler
2027 );
2028}
2029
2030template <typename config>
2031void connection<config>::handle_write_frame(lib::error_code const & ec)
2032{
2033 if (m_alog->static_test(log::alevel::devel)) {
2034 m_alog->write(log::alevel::devel,"connection handle_write_frame");
2035 }
2036
2037 bool terminal = m_current_msgs.back()->get_terminal();
2038
2039 m_send_buffer.clear();
2040 m_current_msgs.clear();
2041 // TODO: recycle instead of deleting
2042
2043 if (ec) {
2044 log_err(log::elevel::fatal,"handle_write_frame",ec);
2045 this->terminate(ec);
2046 return;
2047 }
2048
2049 if (terminal) {
2050 this->terminate(lib::error_code());
2051 return;
2052 }
2053
2054 bool needs_writing = false;
2055 {
2056 scoped_lock_type lock(m_write_lock);
2057
2058 // release write flag
2059 m_write_flag = false;
2060
2061 needs_writing = !m_send_queue.empty();
2062 }
2063
2064 if (needs_writing) {
2065 transport_con_type::dispatch(lib::bind(
2068 ));
2069 }
2070}
2071
2072template <typename config>
2073std::vector<int> const & connection<config>::get_supported_versions() const
2074{
2075 return versions_supported;
2076}
2077
2078template <typename config>
2079void connection<config>::process_control_frame(typename config::message_type::ptr msg)
2080{
2081 m_alog->write(log::alevel::devel,"process_control_frame");
2082
2083 frame::opcode::value op = msg->get_opcode();
2084 lib::error_code ec;
2085
2086 std::stringstream s;
2087 s << "Control frame received with opcode " << op;
2088 m_alog->write(log::alevel::control,s.str());
2089
2090 if (m_state == session::state::closed) {
2091 m_elog->write(log::elevel::warn,"got frame in state closed");
2092 return;
2093 }
2094 if (op != frame::opcode::CLOSE && m_state != session::state::open) {
2095 m_elog->write(log::elevel::warn,"got non-close frame in state closing");
2096 return;
2097 }
2098
2099 if (op == frame::opcode::PING) {
2100 bool should_reply = true;
2101
2102 if (m_ping_handler) {
2103 should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
2104 }
2105
2106 if (should_reply) {
2107 this->pong(msg->get_payload(),ec);
2108 if (ec) {
2109 log_err(log::elevel::devel,"Failed to send response pong",ec);
2110 }
2111 }
2112 } else if (op == frame::opcode::PONG) {
2113 if (m_ping_timer) {
2114 m_ping_timer->cancel();
2115 }
2116 if (m_pong_handler) {
2117 m_pong_handler(m_connection_hdl, msg->get_payload());
2118 }
2119 } else if (op == frame::opcode::CLOSE) {
2120 m_alog->write(log::alevel::devel,"got close frame");
2121 // record close code and reason somewhere
2122
2123 m_remote_close_code = close::extract_code(msg->get_payload(),ec);
2124 if (ec) {
2125 s.str("");
2126 if (config::drop_on_protocol_error) {
2127 s << "Received invalid close code " << m_remote_close_code
2128 << " dropping connection per config.";
2129 m_elog->write(log::elevel::devel,s.str());
2130 this->terminate(ec);
2131 } else {
2132 s << "Received invalid close code " << m_remote_close_code
2133 << " sending acknowledgement and closing";
2134 m_elog->write(log::elevel::devel,s.str());
2135 ec = send_close_ack(close::status::protocol_error,
2136 "Invalid close code");
2137 if (ec) {
2138 log_err(log::elevel::devel,"send_close_ack",ec);
2139 }
2140 }
2141 return;
2142 }
2143
2144 m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
2145 if (ec) {
2146 if (config::drop_on_protocol_error) {
2147 m_elog->write(log::elevel::devel,
2148 "Received invalid close reason. Dropping connection per config");
2149 this->terminate(ec);
2150 } else {
2151 m_elog->write(log::elevel::devel,
2152 "Received invalid close reason. Sending acknowledgement and closing");
2153 ec = send_close_ack(close::status::protocol_error,
2154 "Invalid close reason");
2155 if (ec) {
2156 log_err(log::elevel::devel,"send_close_ack",ec);
2157 }
2158 }
2159 return;
2160 }
2161
2162 if (m_state == session::state::open) {
2163 s.str("");
2164 s << "Received close frame with code " << m_remote_close_code
2165 << " and reason " << m_remote_close_reason;
2166 m_alog->write(log::alevel::devel,s.str());
2167
2168 ec = send_close_ack();
2169 if (ec) {
2170 log_err(log::elevel::devel,"send_close_ack",ec);
2171 }
2172 } else if (m_state == session::state::closing && !m_was_clean) {
2173 // ack of our close
2174 m_alog->write(log::alevel::devel, "Got acknowledgement of close");
2175
2176 m_was_clean = true;
2177
2178 // If we are a server terminate the connection now. Clients should
2179 // leave the connection open to give the server an opportunity to
2180 // initiate the TCP close. The client's timer will handle closing
2181 // its side of the connection if the server misbehaves.
2182 //
2183 // TODO: different behavior if the underlying transport doesn't
2184 // support timers?
2185 if (m_is_server) {
2186 terminate(lib::error_code());
2187 }
2188 } else {
2189 // spurious, ignore
2190 m_elog->write(log::elevel::devel, "Got close frame in wrong state");
2191 }
2192 } else {
2193 // got an invalid control opcode
2194 m_elog->write(log::elevel::devel, "Got control frame with invalid opcode");
2195 // initiate protocol error shutdown
2196 }
2197}
2198
2199template <typename config>
2200lib::error_code connection<config>::send_close_ack(close::status::value code,
2201 std::string const & reason)
2202{
2203 return send_close_frame(code,reason,true,m_is_server);
2204}
2205
2206template <typename config>
2207lib::error_code connection<config>::send_close_frame(close::status::value code,
2208 std::string const & reason, bool ack, bool terminal)
2209{
2210 m_alog->write(log::alevel::devel,"send_close_frame");
2211
2212 // check for special codes
2213
2214 // If silent close is set, respect it and blank out close information
2215 // Otherwise use whatever has been specified in the parameters. If
2216 // parameters specifies close::status::blank then determine what to do
2217 // based on whether or not this is an ack. If it is not an ack just
2218 // send blank info. If it is an ack then echo the close information from
2219 // the remote endpoint.
2220 if (config::silent_close) {
2221 m_alog->write(log::alevel::devel,"closing silently");
2222 m_local_close_code = close::status::no_status;
2223 m_local_close_reason.clear();
2224 } else if (code != close::status::blank) {
2225 m_alog->write(log::alevel::devel,"closing with specified codes");
2226 m_local_close_code = code;
2227 m_local_close_reason = reason;
2228 } else if (!ack) {
2229 m_alog->write(log::alevel::devel,"closing with no status code");
2230 m_local_close_code = close::status::no_status;
2231 m_local_close_reason.clear();
2232 } else if (m_remote_close_code == close::status::no_status) {
2233 m_alog->write(log::alevel::devel,
2234 "acknowledging a no-status close with normal code");
2235 m_local_close_code = close::status::normal;
2236 m_local_close_reason.clear();
2237 } else {
2238 m_alog->write(log::alevel::devel,"acknowledging with remote codes");
2239 m_local_close_code = m_remote_close_code;
2240 m_local_close_reason = m_remote_close_reason;
2241 }
2242
2243 std::stringstream s;
2244 s << "Closing with code: " << m_local_close_code << ", and reason: "
2245 << m_local_close_reason;
2246 m_alog->write(log::alevel::devel,s.str());
2247
2248 message_ptr msg = m_msg_manager->get_message();
2249 if (!msg) {
2250 return error::make_error_code(error::no_outgoing_buffers);
2251 }
2252
2253 lib::error_code ec = m_processor->prepare_close(m_local_close_code,
2254 m_local_close_reason,msg);
2255 if (ec) {
2256 return ec;
2257 }
2258
2259 // Messages flagged terminal will result in the TCP connection being dropped
2260 // after the message has been written. This is typically used when servers
2261 // send an ack and when any endpoint encounters a protocol error
2262 if (terminal) {
2263 msg->set_terminal(true);
2264 }
2265
2266 m_state = session::state::closing;
2267
2268 // Cancel any outstanding ping timers. Once we are in state closing the
2269 // library no longer processes non-close frames, so any pongs will be
2270 // dropped.
2271 if (m_ping_timer) {
2272 m_ping_timer->cancel();
2273 }
2274
2275 if (ack) {
2276 m_was_clean = true;
2277 }
2278
2279 // Start a timer so we don't wait forever for the acknowledgement close
2280 // frame
2281 if (m_close_handshake_timeout_dur > 0) {
2282 m_handshake_timer = transport_con_type::set_timer(
2283 m_close_handshake_timeout_dur,
2284 lib::bind(
2285 &type::handle_close_handshake_timeout,
2287 lib::placeholders::_1
2288 )
2289 );
2290 }
2291
2292 bool needs_writing = false;
2293 {
2294 scoped_lock_type lock(m_write_lock);
2295 write_push(msg);
2296 needs_writing = !m_write_flag && !m_send_queue.empty();
2297 }
2298
2299 if (needs_writing) {
2300 transport_con_type::dispatch(lib::bind(
2303 ));
2304 }
2305
2306 return lib::error_code();
2307}
2308
2309template <typename config>
2310typename connection<config>::processor_ptr
2311connection<config>::get_processor(int version) const {
2312 // TODO: allow disabling certain versions
2313
2314 processor_ptr p;
2315
2316 switch (version) {
2317 case 0:
2318 p = lib::make_shared<processor::hybi00<config> >(
2319 transport_con_type::is_secure(),
2320 m_is_server,
2321 m_msg_manager
2322 );
2323 break;
2324 case 7:
2325 p = lib::make_shared<processor::hybi07<config> >(
2326 transport_con_type::is_secure(),
2327 m_is_server,
2328 m_msg_manager,
2329 lib::ref(m_rng)
2330 );
2331 break;
2332 case 8:
2333 p = lib::make_shared<processor::hybi08<config> >(
2334 transport_con_type::is_secure(),
2335 m_is_server,
2336 m_msg_manager,
2337 lib::ref(m_rng)
2338 );
2339 break;
2340 case 13:
2341 p = lib::make_shared<processor::hybi13<config> >(
2342 transport_con_type::is_secure(),
2343 m_is_server,
2344 m_msg_manager,
2345 lib::ref(m_rng)
2346 );
2347 break;
2348 default:
2349 return p;
2350 }
2351
2352 // Settings not configured by the constructor
2353 p->set_max_message_size(m_max_message_size);
2354
2355 return p;
2356}
2357
2358template <typename config>
2359void connection<config>::write_push(typename config::message_type::ptr msg)
2360{
2361 if (!msg) {
2362 return;
2363 }
2364
2365 m_send_buffer_size += msg->get_payload().size();
2366 m_send_queue.push(msg);
2367
2368 if (m_alog->static_test(log::alevel::devel)) {
2369 std::stringstream s;
2370 s << "write_push: message count: " << m_send_queue.size()
2371 << " buffer size: " << m_send_buffer_size;
2372 m_alog->write(log::alevel::devel,s.str());
2373 }
2374}
2375
2376template <typename config>
2377typename config::message_type::ptr connection<config>::write_pop()
2378{
2379 message_ptr msg;
2380
2381 if (m_send_queue.empty()) {
2382 return msg;
2383 }
2384
2385 msg = m_send_queue.front();
2386
2387 m_send_buffer_size -= msg->get_payload().size();
2388 m_send_queue.pop();
2389
2390 if (m_alog->static_test(log::alevel::devel)) {
2391 std::stringstream s;
2392 s << "write_pop: message count: " << m_send_queue.size()
2393 << " buffer size: " << m_send_buffer_size;
2394 m_alog->write(log::alevel::devel,s.str());
2395 }
2396 return msg;
2397}
2398
2399template <typename config>
2400void connection<config>::log_open_result()
2401{
2402 std::stringstream s;
2403
2404 int version;
2405 if (!processor::is_websocket_handshake(m_request)) {
2406 version = -1;
2407 } else {
2408 version = processor::get_websocket_version(m_request);
2409 }
2410
2411 // Connection Type
2412 s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
2413
2414 // Remote endpoint address
2415 s << transport_con_type::get_remote_endpoint() << " ";
2416
2417 // Version string if WebSocket
2418 if (version != -1) {
2419 s << "v" << version << " ";
2420 }
2421
2422 // User Agent
2423 std::string ua = m_request.get_header("User-Agent");
2424 if (ua.empty()) {
2425 s << "\"\" ";
2426 } else {
2427 // check if there are any quotes in the user agent
2428 s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2429 }
2430
2431 // URI
2432 s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
2433
2434 // Status code
2435 s << m_response.get_status_code();
2436
2437 m_alog->write(log::alevel::connect,s.str());
2438}
2439
2440template <typename config>
2441void connection<config>::log_close_result()
2442{
2443 std::stringstream s;
2444
2445 s << "Disconnect "
2446 << "close local:[" << m_local_close_code
2447 << (m_local_close_reason.empty() ? "" : ","+m_local_close_reason)
2448 << "] remote:[" << m_remote_close_code
2449 << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]";
2450
2451 m_alog->write(log::alevel::disconnect,s.str());
2452}
2453
2454template <typename config>
2455void connection<config>::log_fail_result()
2456{
2457 std::stringstream s;
2458
2459 int version = processor::get_websocket_version(m_request);
2460
2461 // Connection Type
2462 s << "WebSocket Connection ";
2463
2464 // Remote endpoint address & WebSocket version
2465 s << transport_con_type::get_remote_endpoint();
2466 if (version < 0) {
2467 s << " -";
2468 } else {
2469 s << " v" << version;
2470 }
2471
2472 // User Agent
2473 std::string ua = m_request.get_header("User-Agent");
2474 if (ua.empty()) {
2475 s << " \"\" ";
2476 } else {
2477 // check if there are any quotes in the user agent
2478 s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2479 }
2480
2481 // URI
2482 s << (m_uri ? m_uri->get_resource() : "-");
2483
2484 // HTTP Status code
2485 s << " " << m_response.get_status_code();
2486
2487 // WebSocket++ error code & reason
2488 s << " " << m_ec << " " << m_ec.message();
2489
2490 m_alog->write(log::alevel::fail,s.str());
2491}
2492
2493template <typename config>
2494void connection<config>::log_http_result() {
2495 std::stringstream s;
2496
2497 if (processor::is_websocket_handshake(m_request)) {
2498 m_alog->write(log::alevel::devel,"Call to log_http_result for WebSocket");
2499 return;
2500 }
2501
2502 // Connection Type
2503 s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host"))
2504 << " " << transport_con_type::get_remote_endpoint()
2505 << " \"" << m_request.get_method()
2506 << " " << (m_uri ? m_uri->get_resource() : "-")
2507 << " " << m_request.get_version() << "\" " << m_response.get_status_code()
2508 << " " << m_response.get_body().size();
2509
2510 // User Agent
2511 std::string ua = m_request.get_header("User-Agent");
2512 if (ua.empty()) {
2513 s << " \"\" ";
2514 } else {
2515 // check if there are any quotes in the user agent
2516 s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
2517 }
2518
2519 m_alog->write(log::alevel::http,s.str());
2520}
2521
2522} // namespace websocketpp
2523
2524#endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_INITIALIZER_LISTS_
Concurrency policy that uses std::mutex / boost::mutex.
Definition basic.hpp:37
Stub for user supplied base class.
Represents an individual WebSocket connection.
ptr get_shared()
Get a shared pointer to this component.
void handle_interrupt()
Transport interrupt callback.
void set_message_handler(message_handler h)
Set message handler.
lib::error_code interrupt()
Asynchronously invoke handler::on_interrupt.
void start()
Start the connection state machine.
std::string const & get_request_body() const
Retrieve a request body.
config::transport_type::transport_con_type transport_con_type
Type of the transport component of this connection.
void set_close_handler(close_handler h)
Set close handler.
lib::error_code send(message_ptr msg)
Add a message to the outgoing send queue.
config::alog_type alog_type
Type of the access logging policy.
http::status_code::value get_response_code() const
Get response HTTP status code.
lib::error_code defer_http_response()
Defer HTTP Response until later (Exception free).
lib::error_code resume_reading()
Resume reading of new data.
bool get_secure() const
Returns the secure flag from the connection URI.
void set_close_handshake_timeout(long dur)
Set close handshake timeout.
size_t get_buffered_amount() const
Get the size of the outgoing write buffer (in payload bytes).
std::string const & get_origin() const
Return the same origin policy origin value from the opening request.
std::string const & get_host() const
Returns the host component of the connection URI.
response_type const & get_response() const
Get response object.
void set_max_message_size(size_t new_value)
Set maximum message size.
std::string const & get_resource() const
Returns the resource component of the connection URI.
lib::error_code process_handshake_request()
void handle_pause_reading()
Pause reading callback.
std::vector< int > const & get_supported_versions() const
Get array of WebSocket protocol versions that this connection supports.
lib::error_code send(void const *payload, size_t len, frame::opcode::value op=frame::opcode::binary)
Send a message (raw array overload).
uri_ptr get_uri() const
Gets the connection URI.
connection_hdl get_handle() const
Get Connection Handle.
void send_http_response(lib::error_code &ec)
Send deferred HTTP Response (exception free).
connection< config > type
Type of this connection.
void set_http_handler(http_handler h)
Set http handler.
std::string const & get_response_msg() const
Get response HTTP status message.
config::elog_type elog_type
Type of the error logging policy.
message_ptr get_message(websocketpp::frame::opcode::value op, size_t size) const
Get a message buffer.
void handle_write_frame(lib::error_code const &ec)
Process the results of a frame write operation and start the next write.
close::status::value get_local_close_code() const
Get the WebSocket close code sent by this endpoint.
lib::error_code get_ec() const
Get the internal error code for a closed/failed connection.
void set_status(http::status_code::value code, lib::error_code &ec)
Set response status code and message (exception free).
void set_fail_handler(fail_handler h)
Set fail handler.
void write_frame()
Checks if there are frames in the send queue and if there are sends one.
session::state::value get_state() const
Return the connection state.
void set_status(http::status_code::value code)
Set response status code and message (exception).
size_t get_max_http_body_size() const
Get maximum HTTP message body size.
config::concurrency_type concurrency_type
Type of the concurrency component of this connection.
transport_con_type::timer_ptr timer_ptr
Type of a pointer to a transport timer handle.
void set_validate_handler(validate_handler h)
Set validate handler.
void set_interrupt_handler(interrupt_handler h)
Set interrupt handler.
size_t buffered_amount() const
Get the size of the outgoing write buffer (in payload bytes).
void send_http_response()
Send deferred HTTP Response.
close::status::value get_remote_close_code() const
Get the WebSocket close code sent by the remote endpoint.
void set_open_handler(open_handler h)
Set open handler.
bool is_server() const
Get whether or not this connection is part of a server or client.
lib::shared_ptr< type > ptr
Type of a shared pointer to this connection.
std::string const & get_remote_close_reason() const
Get the WebSocket close reason sent by the remote endpoint.
void set_pong_timeout(long dur)
Set pong timeout.
lib::error_code initialize_processor()
void set_uri(uri_ptr uri)
Sets the connection URI.
void set_termination_handler(termination_handler new_handler)
request_type const & get_request() const
Get request object.
void set_max_http_body_size(size_t new_value)
Set maximum HTTP message body size.
transport_con_type::ptr transport_con_ptr
Type of a shared pointer to the transport component of this connection.
void set_open_handshake_timeout(long dur)
Set open handshake timeout.
lib::error_code pause_reading()
Pause reading of new data.
void handle_resume_reading()
Resume reading callback.
std::vector< std::string > const & get_requested_subprotocols() const
Gets all of the subprotocols requested by the client.
std::string const & get_subprotocol() const
Gets the negotiated subprotocol.
void set_handle(connection_hdl hdl)
Set Connection Handle.
void read_frame()
Issue a new transport read unless reading is paused.
uint16_t get_port() const
Returns the port component of the connection URI.
std::string const & get_local_close_reason() const
Get the WebSocket close reason sent by this endpoint.
size_t get_max_message_size() const
Get maximum message size.
config::rng_type rng_type
Type of RNG.
Stub for user supplied base class.
Creates and manages connections associated with a WebSocket endpoint.
Definition endpoint.hpp:44
void set_max_http_body_size(size_t new_value)
Set maximum HTTP message body size.
Definition endpoint.hpp:468
connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code &ec)
Retrieves a connection_ptr from a connection_hdl (exception free).
Definition endpoint.hpp:644
config::concurrency_type concurrency_type
Type of the concurrency component of this endpoint.
Definition endpoint.hpp:53
config::alog_type alog_type
Type of access logger.
Definition endpoint.hpp:77
alog_type & get_alog()
Get reference to access logger.
Definition endpoint.hpp:263
transport_con_type::ptr transport_con_ptr
Definition endpoint.hpp:67
std::string get_user_agent() const
Returns the user agent string that this endpoint will use.
Definition endpoint.hpp:171
void clear_access_channels(log::level channels)
Clear Access logging channels.
Definition endpoint.hpp:233
void resume_reading(connection_hdl hdl)
Resume reading of new data.
config::elog_type elog_type
Type of error logger.
Definition endpoint.hpp:75
elog_type & get_elog()
Get reference to error logger.
Definition endpoint.hpp:271
bool is_server() const
Returns whether or not this endpoint is a server.
Definition endpoint.hpp:207
~endpoint()
Destructor.
Definition endpoint.hpp:114
void set_close_handshake_timeout(long dur)
Set close handshake timeout.
Definition endpoint.hpp:379
connection_type::message_ptr message_ptr
Type of message pointers that this endpoint uses.
Definition endpoint.hpp:72
void set_access_channels(log::level channels)
Set Access logging channel.
Definition endpoint.hpp:222
void set_max_message_size(size_t new_value)
Set default maximum message size.
Definition endpoint.hpp:434
size_t get_max_message_size() const
Get default maximum message size.
Definition endpoint.hpp:417
connection_ptr get_con_from_hdl(connection_hdl hdl)
Retrieves a connection_ptr from a connection_hdl (exception version).
Definition endpoint.hpp:655
connection connection_type
Type of the connections that this endpoint creates.
Definition endpoint.hpp:56
void set_error_channels(log::level channels)
Set Error logging channel.
Definition endpoint.hpp:244
config::transport_type transport_type
Type of the transport component of this endpoint.
Definition endpoint.hpp:51
void clear_error_channels(log::level channels)
Clear Error logging channels.
Definition endpoint.hpp:255
void resume_reading(connection_hdl hdl, lib::error_code &ec)
Resume reading of new data (exception free).
connection_type::ptr connection_ptr
Shared pointer to connection_type.
Definition endpoint.hpp:58
transport_type::transport_con_type transport_con_type
Definition endpoint.hpp:64
concurrency_type::mutex_type mutex_type
Type of our concurrency policy's mutex object.
Definition endpoint.hpp:82
void interrupt(connection_hdl hdl, lib::error_code &ec)
void set_pong_timeout(long dur)
Set pong timeout.
Definition endpoint.hpp:401
size_t get_max_http_body_size() const
Get maximum HTTP message body size.
Definition endpoint.hpp:451
void set_open_handshake_timeout(long dur)
Set open handshake timeout.
Definition endpoint.hpp:354
config::rng_type rng_type
Type of RNG.
Definition endpoint.hpp:85
concurrency_type::scoped_lock_type scoped_lock_type
Type of our concurrency policy's scoped lock object.
Definition endpoint.hpp:80
void send_http_response(connection_hdl hdl, lib::error_code &ec)
Send deferred HTTP Response.
void pause_reading(connection_hdl hdl, lib::error_code &ec)
Pause reading of new data (exception free).
void send_http_response(connection_hdl hdl)
Send deferred HTTP Response (exception free).
void pause_reading(connection_hdl hdl)
Pause reading of new data.
connection_type::message_handler message_handler
Type of message_handler.
Definition endpoint.hpp:70
connection_type::weak_ptr connection_weak_ptr
Weak pointer to connection type.
Definition endpoint.hpp:60
Stub class for use when disabling permessage_deflate extension.
Definition disabled.hpp:53
err_str_pair negotiate(http::attribute_list const &)
Negotiate extension.
Definition disabled.hpp:65
std::string generate_offer() const
Generate extension offer.
Definition disabled.hpp:104
void set_max_message_size(size_t)
Set maximum decompressed message size (no-op).
Definition disabled.hpp:116
lib::error_code init(bool)
Initialize state.
Definition disabled.hpp:81
HTTP parser error category.
An exception type specific to HTTP errors.
header_list const & get_headers() const
Return a list of all HTTP headers.
Definition parser.hpp:213
std::string const & get_body() const
Get HTTP body.
Definition parser.hpp:515
bool body_ready() const
Check if the parser is done parsing the body.
Definition parser.hpp:618
void set_max_body_size(size_t value)
Set body size limit.
Definition parser.hpp:555
bool prepare_body(lib::error_code &ec)
Prepare the parser to begin parsing body data.
Definition parser.hpp:145
std::string raw_headers() const
Generate and return the HTTP headers as a string.
Definition parser.hpp:217
size_t process_body(char const *buf, size_t len, lib::error_code &ec)
Process body data.
Definition parser.hpp:174
std::string const & get_version() const
Get the HTTP version string.
Definition parser.hpp:410
size_t get_max_body_size() const
Get body size limit.
Definition parser.hpp:542
lib::error_code process_header(std::string::iterator begin, std::string::iterator end)
Process a header line.
Definition parser.hpp:193
Stores, parses, and manipulates HTTP requests.
Definition request.hpp:50
std::string raw() const
Returns the full raw request (including the body).
Definition request.hpp:195
std::string const & get_uri() const
Return the requested URI.
Definition request.hpp:123
std::string const & get_method() const
Return the request method.
Definition request.hpp:107
bool ready() const
Returns whether or not the request is ready for reading.
Definition request.hpp:85
size_t consume(char const *buf, size_t len, lib::error_code &ec)
Process bytes in the input buffer.
Definition request.hpp:41
std::string raw_head() const
Returns the raw request headers only (similar to an HTTP HEAD request).
Definition request.hpp:205
Stores, parses, and manipulates HTTP responses.
Definition response.hpp:57
std::string raw() const
Returns the full raw response.
Definition response.hpp:246
size_t consume(char const *buf, size_t len, lib::error_code &ec)
Process bytes in the input buffer.
Definition response.hpp:42
bool headers_ready() const
Returns true if the response headers are fully parsed.
Definition response.hpp:149
size_t consume(std::istream &s, lib::error_code &ec)
Process bytes in the input buffer (istream version).
Definition response.hpp:189
lib::error_code set_status(status_code::value code)
Set response status code and message.
Definition response.hpp:259
bool ready() const
Returns true if the response is ready.
Definition response.hpp:144
const std::string & get_status_msg() const
Return the response status message.
Definition response.hpp:190
status_code::value get_status_code() const
Return the response status code.
Definition response.hpp:185
Basic logger that outputs to an ostream.
Definition basic.hpp:59
void write(level channel, char const *msg)
Write a cstring message to the given channel.
Definition basic.hpp:151
basic(basic< concurrency, names > const &other)
Copy constructor.
Definition basic.hpp:87
~basic()
Destructor.
Definition basic.hpp:84
bool recycle(message *)
Recycle a message.
Definition alloc.hpp:80
message_ptr get_message(frame::opcode::value op, size_t size)
Get a message buffer with specified size and opcode.
Definition alloc.hpp:66
message_ptr get_message()
Get an empty message buffer.
Definition alloc.hpp:55
con_msg_man_ptr get_manager() const
Get a pointer to a connection message manager.
Definition alloc.hpp:96
Represents a buffer for a single WebSocket message.
Definition message.hpp:84
message(const con_msg_man_ptr manager, frame::opcode::value op, size_t size=128)
Construct a message and fill in some values.
Definition message.hpp:107
std::string & get_raw_payload()
Get a non-const reference to the payload string.
Definition message.hpp:254
bool recycle()
Recycle the message.
Definition message.hpp:316
bool get_compressed() const
Return whether or not the message is flagged as compressed.
Definition message.hpp:143
bool get_terminal() const
Get whether or not the message is terminal.
Definition message.hpp:169
std::string const & get_header() const
Return the prepared frame header.
Definition message.hpp:224
void set_payload(void const *payload, size_t len)
Set payload data.
Definition message.hpp:275
bool get_fin() const
Read the fin bit.
Definition message.hpp:195
void append_payload(void const *payload, size_t len)
Append payload data.
Definition message.hpp:298
void set_opcode(frame::opcode::value op)
Set the opcode.
Definition message.hpp:215
void set_prepared(bool value)
Set or clear the flag that indicates that the message has been prepared.
Definition message.hpp:135
frame::opcode::value get_opcode() const
Return the message opcode.
Definition message.hpp:210
void set_terminal(bool value)
Set the terminal flag.
Definition message.hpp:181
bool get_prepared() const
Return whether or not the message has been prepared for sending.
Definition message.hpp:125
void set_compressed(bool value)
Set or clear the compression flag.
Definition message.hpp:156
message(const con_msg_man_ptr manager)
Construct an empty message.
Definition message.hpp:96
void set_fin(bool value)
Set the fin bit.
Definition message.hpp:205
std::string const & get_payload() const
Get a reference to the payload string.
Definition message.hpp:246
Category for processor errors.
Definition base.hpp:161
Processor for Hybi Draft version 00.
Definition hybi00.hpp:54
Processor for Hybi Draft version 07.
Definition hybi07.hpp:44
Processor for Hybi Draft version 08.
Definition hybi08.hpp:44
Processor for Hybi version 13 (RFC6455).
Definition hybi13.hpp:54
WebSocket protocol processor abstract base class.
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)=0
Prepare a data message for writing.
virtual size_t get_bytes_needed() const
virtual size_t consume(uint8_t *buf, size_t len, lib::error_code &ec)=0
process new websocket connection bytes
virtual err_str_pair negotiate_extensions(response_type const &)
Initializes extensions based on the Sec-WebSocket-Extensions header.
virtual void handle_max_message_size_changed(size_t)
Hook invoked when the maximum message size changes.
virtual int get_version() const =0
Get the protocol version of this processor.
virtual std::string get_raw(response_type const &request) const =0
Given a completed response, get the raw bytes to put on the wire.
virtual bool ready() const =0
Checks if there is a message ready.
virtual lib::error_code validate_handshake(request_type const &request) const =0
validate a WebSocket handshake request for this version
virtual uri_ptr get_uri(request_type const &request) const =0
Extracts client uri from a handshake request.
virtual err_str_pair negotiate_extensions(request_type const &)
Initializes extensions based on the Sec-WebSocket-Extensions header.
virtual bool has_permessage_compress() const
Returns whether or not the permessage_compress extension is implemented.
virtual lib::error_code validate_server_handshake_response(request_type const &req, response_type &res) const =0
Validate the server's response to an outgoing handshake request.
virtual lib::error_code client_handshake_request(request_type &req, uri_ptr uri, std::vector< std::string > const &subprotocols) const =0
Fill in an HTTP request for an outgoing connection handshake.
virtual lib::error_code extract_subprotocols(const request_type &req, std::vector< std::string > &subprotocol_list)=0
Extracts requested subprotocols from a handshake request.
virtual bool get_error() const =0
Tests whether the processor is in a fatal error state.
void set_max_message_size(size_t new_value)
Set maximum message size.
virtual std::string const & get_origin(request_type const &request) const =0
Return the value of the header containing the CORS origin.
size_t get_max_message_size() const
Get maximum message size.
virtual message_ptr get_message()=0
Retrieves the most recently processed message.
Thread safe stub "random" integer generator.
Definition none.hpp:46
int_type operator()()
advances the engine's state and returns the generated value
Definition none.hpp:51
Server endpoint role based on the given config.
transport_type::transport_con_type transport_con_type
Type of the connection transport component.
connection_type::ptr connection_ptr
Type of a shared pointer to the connections this server will create.
server< config > type
Type of this endpoint.
void start_accept(accept_loop_handler completion_handler)
Starts the server's async connection acceptance loop (exception free).
endpoint< connection_type, config > endpoint_type
Type of the endpoint component of this server.
config::transport_type transport_type
Type of the endpoint transport component.
connection< config > connection_type
Type of the connections this server will create.
connection_ptr get_connection(lib::error_code &ec)
Create and initialize a new connection.
connection_ptr get_connection()
Create and initialize a new connection.
lib::function< void(lib::error_code const &, lib::error_code const &)> accept_loop_handler
The type and signature of the callback passed to the start_accept method.
config::concurrency_type concurrency_type
Type of the endpoint concurrency component.
transport_con_type::ptr transport_con_ptr
Type of a shared pointer to the connection transport component.
Basic Asio connection socket component.
Definition none.hpp:58
io_context_ptr io_service_ptr
Type of a pointer to the Asio io_context being used (deprecated).
Definition none.hpp:71
lib::asio::ip::tcp::socket & get_raw_socket()
Retrieve a pointer to the underlying socket.
Definition none.hpp:129
lib::shared_ptr< type > ptr
Type of a shared pointer to this connection socket component.
Definition none.hpp:63
void set_socket_init_handler(socket_init_handler h)
Set the socket initialization handler.
Definition none.hpp:105
connection type
Type of this connection socket component.
Definition none.hpp:61
lib::shared_ptr< lib::asio::io_context::strand > strand_ptr
Type of a pointer to the Asio io_context strand being used.
Definition none.hpp:73
lib::asio::ip::tcp::socket & get_socket()
Retrieve a pointer to the underlying socket.
Definition none.hpp:113
bool is_secure() const
Check whether or not this connection is secure.
Definition none.hpp:93
lib::shared_ptr< socket_type > socket_ptr
Type of a shared pointer to the socket being used.
Definition none.hpp:77
static lib::error_code translate_ec(lib::error_code ec)
Definition none.hpp:298
static lib::error_code translate_ec(ErrorCodeType)
Translate any security policy specific information about an error code.
Definition none.hpp:290
void set_handle(connection_hdl hdl)
Sets the connection handle.
Definition none.hpp:240
lib::asio::error_code cancel_socket()
Cancel all async operations on this socket.
Definition none.hpp:253
void pre_init(init_handler callback)
Pre-initialize security policy.
Definition none.hpp:206
std::string get_remote_endpoint(lib::error_code &ec) const
Get the remote endpoint address.
Definition none.hpp:143
lib::asio::io_context * io_context_ptr
Type of a pointer to the Asio io_context being used.
Definition none.hpp:66
void post_init(init_handler callback)
Post-initialize security policy.
Definition none.hpp:229
lib::asio::ip::tcp::socket socket_type
Type of the ASIO socket being used.
Definition none.hpp:75
lib::error_code init_asio(io_context_ptr context, strand_ptr, bool)
Perform one time initializations.
Definition none.hpp:171
ptr get_shared()
Get a shared pointer to this component.
Definition none.hpp:85
lib::asio::ip::tcp::socket & get_next_layer()
Retrieve a pointer to the underlying socket.
Definition none.hpp:121
Basic ASIO endpoint socket component.
Definition none.hpp:323
void set_socket_init_handler(socket_init_handler h)
Set socket init handler.
Definition none.hpp:352
lib::error_code init(socket_con_ptr scon)
Initialize a connection.
Definition none.hpp:365
endpoint type
The type of this endpoint socket component.
Definition none.hpp:326
bool is_secure() const
Checks whether the endpoint creates secure connections.
Definition none.hpp:340
connection socket_con_type
The type of the corresponding connection socket component.
Definition none.hpp:329
Asio based connection transport component.
void set_proxy_timeout(long duration)
Set the proxy timeout duration (exception).
void set_tcp_post_init_handler(tcp_init_handler h)
Sets the tcp post init handler.
void set_proxy_timeout(long duration, lib::error_code &ec)
Set the proxy timeout duration (exception free).
config::elog_type elog_type
Type of this transport's error logging policy.
strand_ptr get_strand()
Get a pointer to this connection's strand.
void async_read_at_least(size_t num_bytes, char *buf, size_t len, read_handler handler)
read at least num_bytes bytes into buf and then call handler.
socket_con_type::ptr socket_con_ptr
Type of a shared pointer to the socket connection component.
lib::error_code interrupt(interrupt_handler handler)
Trigger the on_interrupt handler.
lib::shared_ptr< lib::asio::io_context::strand > strand_ptr
Type of a pointer to the Asio io_context::strand being used.
config::alog_type alog_type
Type of this transport's access logging policy.
io_context_ptr io_service_ptr
Type of a pointer to the Asio io_context being used (deprecated).
lib::asio::io_context * io_context_ptr
Type of a pointer to the Asio io_context being used.
void handle_async_write(write_handler handler, lib::asio::error_code const &ec, size_t)
Async write callback.
void handle_timer(timer_ptr, timer_handler callback, lib::asio::error_code const &ec)
Timer callback.
void handle_post_init(timer_ptr post_timer, init_handler callback, lib::error_code const &ec)
Post init timeout callback.
lib::error_code init_asio(io_context_ptr io_context)
Finish constructing the transport.
void async_shutdown(shutdown_handler callback)
close and clean up the underlying socket
lib::asio::error_code get_transport_ec() const
Get the internal transport error code for a closed/failed connection.
config::socket_type::socket_con_type socket_con_type
Type of the socket connection component.
void handle_post_init_timeout(timer_ptr, init_handler callback, lib::error_code const &ec)
Post init timeout callback.
connection< config > type
Type of this connection transport component.
void handle_async_shutdown_timeout(timer_ptr, init_handler callback, lib::error_code const &ec)
Async shutdown timeout handler.
lib::shared_ptr< lib::asio::steady_timer > timer_ptr
Type of a pointer to the Asio timer class.
void async_write(const char *buf, size_t len, write_handler handler)
Initiate a potentially asyncronous write of the given buffer.
void async_write(std::vector< buffer > const &bufs, write_handler handler)
Initiate a potentially asyncronous write of the given buffers.
lib::shared_ptr< type > ptr
Type of a shared pointer to this connection transport component.
timer_ptr set_timer(long duration, timer_handler callback)
Call back a function after a period of time.
void set_tcp_init_handler(tcp_init_handler h)
Sets the tcp pre init handler (deprecated).
void handle_proxy_read(init_handler callback, lib::asio::error_code const &ec, size_t)
Proxy read callback.
void set_uri(uri_ptr u)
Set uri hook.
std::string get_remote_endpoint() const
Get the remote endpoint address.
ptr get_shared()
Get a shared pointer to this component.
void cancel_socket_checked()
Cancel the underlying socket and log any errors.
void set_handle(connection_hdl hdl)
Set Connection Handle.
void set_tcp_pre_init_handler(tcp_init_handler h)
Sets the tcp pre init handler.
void init(init_handler callback)
Initialize transport for reading.
connection_hdl get_handle() const
Get the connection handle.
Asio based endpoint transport component.
Definition endpoint.hpp:54
lib::shared_ptr< lib::asio::executor_work_guard< lib::asio::io_context::executor_type > > work_guard_ptr
Type of a shared pointer to an io_context work object.
Definition endpoint.hpp:94
std::size_t run_one()
wraps the run_one method of the internal io_context object
Definition endpoint.hpp:691
socket_type::socket_con_type socket_con_type
Type of the socket connection component.
Definition endpoint.hpp:69
void stop_listening(lib::error_code &ec)
Stop listening (exception free).
Definition endpoint.hpp:566
config::socket_type socket_type
Type of the socket policy.
Definition endpoint.hpp:62
lib::shared_ptr< lib::asio::steady_timer > timer_ptr
Type of timer handle.
Definition endpoint.hpp:92
void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb)
Initiate a new connection.
Definition endpoint.hpp:894
void init_asio()
Initialize asio transport with internal io_context.
Definition endpoint.hpp:262
config::elog_type elog_type
Type of the error logging policy.
Definition endpoint.hpp:64
void init_logging(const lib::shared_ptr< alog_type > &a, const lib::shared_ptr< elog_type > &e)
Initialize logging.
Definition endpoint.hpp:868
lib::asio::io_context * io_context_ptr
Type of a pointer to the ASIO io_context being used.
Definition endpoint.hpp:81
bool is_secure() const
Return whether or not the endpoint produces secure connections.
Definition endpoint.hpp:182
lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code &ec)
Get local TCP endpoint.
Definition endpoint.hpp:419
void set_reuse_addr(bool value)
Sets whether to use the SO_REUSEADDR flag when opening listening sockets.
Definition endpoint.hpp:377
void set_tcp_init_handler(tcp_init_handler h)
Sets the tcp pre init handler (deprecated).
Definition endpoint.hpp:316
void handle_timer(timer_ptr, timer_handler callback, lib::asio::error_code const &ec)
Timer handler.
Definition endpoint.hpp:788
lib::asio::io_context & get_io_service()
Retrieve a reference to the endpoint's io_context (deprecated).
Definition endpoint.hpp:402
void start_perpetual()
Marks the endpoint as perpetual, stopping it from exiting when empty.
Definition endpoint.hpp:732
void stop()
wraps the stop method of the internal io_context object
Definition endpoint.hpp:696
std::size_t run()
wraps the run method of the internal io_context object
Definition endpoint.hpp:683
void set_tcp_post_init_handler(tcp_init_handler h)
Sets the tcp post init handler.
Definition endpoint.hpp:331
void set_listen_backlog(int backlog)
Sets the maximum length of the queue of pending connections.
Definition endpoint.hpp:356
bool stopped() const
wraps the stopped method of the internal io_context object
Definition endpoint.hpp:716
timer_ptr set_timer(long duration, timer_handler callback)
Call back a function after a period of time.
Definition endpoint.hpp:760
socket_con_type::ptr socket_con_ptr
Type of a shared pointer to the socket connection component.
Definition endpoint.hpp:71
lib::error_code init(transport_con_ptr tcon)
Initialize a connection.
asio::connection< config > transport_con_type
Definition endpoint.hpp:75
void init_asio(lib::error_code &ec)
Initialize asio transport with internal io_context (exception free).
Definition endpoint.hpp:239
void async_accept(transport_con_ptr tcon, accept_handler callback)
Accept the next connection attempt and assign it to con.
Definition endpoint.hpp:851
void listen(uint16_t port)
Set up endpoint for listening on a port.
Definition endpoint.hpp:627
endpoint< config > type
Type of this endpoint transport component.
Definition endpoint.hpp:57
void init_asio(io_context_ptr ptr, lib::error_code &ec)
initialize asio transport with external io_context (exception free)
Definition endpoint.hpp:195
config::concurrency_type concurrency_type
Type of the concurrency policy.
Definition endpoint.hpp:60
std::size_t poll()
wraps the poll method of the internal io_context object
Definition endpoint.hpp:701
void stop_perpetual()
Clears the endpoint's perpetual flag, allowing it to exit when empty.
Definition endpoint.hpp:744
void init_asio(io_context_ptr ptr)
initialize asio transport with external io_context
Definition endpoint.hpp:223
lib::shared_ptr< lib::asio::ip::tcp::acceptor > acceptor_ptr
Type of a shared pointer to the acceptor being used.
Definition endpoint.hpp:88
void listen(lib::asio::ip::tcp::endpoint const &ep)
Set up endpoint for listening manually.
Definition endpoint.hpp:589
lib::asio::io_context & get_io_context()
Retrieve a reference to the endpoint's io_context.
Definition endpoint.hpp:392
void handle_resolve_timeout(timer_ptr, connect_handler callback, lib::error_code const &ec)
DNS resolution timeout handler.
Definition endpoint.hpp:989
void listen(lib::asio::ip::tcp::endpoint const &ep, lib::error_code &ec)
Set up endpoint for listening manually (exception free).
Definition endpoint.hpp:436
transport_con_type::ptr transport_con_ptr
Definition endpoint.hpp:78
void reset()
wraps the restart method of the internal io_context object
Definition endpoint.hpp:711
void set_tcp_pre_bind_handler(tcp_pre_bind_handler h)
Sets the tcp pre bind handler.
Definition endpoint.hpp:288
work_guard_ptr work_ptr
Type of a shared pointer to an io_context work object (deprecated).
Definition endpoint.hpp:99
config::alog_type alog_type
Type of the access logging policy.
Definition endpoint.hpp:66
void listen(InternetProtocol const &internet_protocol, uint16_t port)
Set up endpoint for listening with protocol and port.
Definition endpoint.hpp:609
void set_tcp_pre_init_handler(tcp_init_handler h)
Sets the tcp pre init handler.
Definition endpoint.hpp:302
void handle_connect_timeout(transport_con_ptr tcon, timer_ptr, connect_handler callback, lib::error_code const &ec)
Asio connect timeout handler.
void async_accept(transport_con_ptr tcon, accept_handler callback, lib::error_code &ec)
Accept the next connection attempt and assign it to con (exception free).
Definition endpoint.hpp:811
std::size_t poll_one()
wraps the poll_one method of the internal io_context object
Definition endpoint.hpp:706
void listen(InternetProtocol const &internet_protocol, uint16_t port, lib::error_code &ec)
Set up endpoint for listening with protocol and port (exception free).
Definition endpoint.hpp:491
void listen(uint16_t port, lib::error_code &ec)
Set up endpoint for listening on a port (exception free).
Definition endpoint.hpp:510
bool is_listening() const
Check if the endpoint is listening.
Definition endpoint.hpp:678
void stop_listening()
Stop listening.
Definition endpoint.hpp:667
io_context_ptr io_service_ptr
Type of a pointer to the ASIO io_context being used (deprecated).
Definition endpoint.hpp:86
lib::shared_ptr< lib::asio::ip::tcp::resolver > resolver_ptr
Type of a shared pointer to the resolver being used.
Definition endpoint.hpp:90
lib::function< lib::error_code(acceptor_ptr)> tcp_pre_bind_handler
Type of socket pre-bind handler.
Definition endpoint.hpp:102
Asio transport error category.
Definition base.hpp:184
Error category related to asio transport socket policies.
Definition base.hpp:110
lib::shared_ptr< type > ptr
Type of a shared pointer to this connection transport component.
connection_hdl get_handle() const
Get the connection handle.
config::alog_type alog_type
Type of this transport's access logging policy.
lib::error_code dispatch(dispatch_handler handler)
Call given handler back within the transport's event system (if present).
void async_shutdown(transport::shutdown_handler handler)
Perform cleanup on socket shutdown_handler.
void set_write_handler(write_handler h)
Sets the write handler.
void set_secure(bool value)
Set whether or not this connection is secure.
void set_shutdown_handler(shutdown_handler h)
Sets the shutdown handler.
connection< config > type
Type of this connection transport component.
config::elog_type elog_type
Type of this transport's error logging policy.
void fatal_error()
Signal transport error.
size_t read_some(char const *buf, size_t len)
Manual input supply (read some).
size_t read_all(char const *buf, size_t len)
Manual input supply (read all).
void async_write(char const *buf, size_t len, transport::write_handler handler)
Asyncronous Transport Write.
size_t readsome(char const *buf, size_t len)
Manual input supply (DEPRECATED).
config::concurrency_type concurrency_type
transport concurrency policy
void init(init_handler handler)
Initialize the connection transport.
timer_ptr set_timer(long, timer_handler)
Call back a function after a period of time.
friend std::istream & operator>>(std::istream &in, type &t)
Overloaded stream input operator.
void set_vector_write_handler(vector_write_handler h)
Sets the vectored write handler.
bool is_secure() const
Tests whether or not the underlying transport is secure.
std::string get_remote_endpoint() const
Get human readable remote endpoint address.
void set_handle(connection_hdl hdl)
Set Connection Handle.
void register_ostream(std::ostream *o)
Register a std::ostream with the transport for writing output.
void async_read_at_least(size_t num_bytes, char *buf, size_t len, read_handler handler)
Initiate an async_read for at least num_bytes bytes into buf.
void async_write(std::vector< buffer > const &bufs, transport::write_handler handler)
Asyncronous Transport Write (scatter-gather).
ptr get_shared()
Get a shared pointer to this component.
iostream::connection< config > transport_con_type
Definition endpoint.hpp:62
config::elog_type elog_type
Type of this endpoint's error logging policy.
Definition endpoint.hpp:56
void set_write_handler(write_handler h)
Sets the write handler.
Definition endpoint.hpp:134
void set_shutdown_handler(shutdown_handler h)
Sets the shutdown handler.
Definition endpoint.hpp:154
bool is_secure() const
Tests whether or not the underlying transport is secure.
Definition endpoint.hpp:116
lib::shared_ptr< type > ptr
Type of a pointer to this endpoint transport component.
Definition endpoint.hpp:51
transport_con_type::ptr transport_con_ptr
Definition endpoint.hpp:65
void async_connect(transport_con_ptr, uri_ptr, connect_handler cb)
Initiate a new connection.
Definition endpoint.hpp:183
lib::error_code init(transport_con_ptr tcon)
Initialize a connection.
Definition endpoint.hpp:197
void init_logging(lib::shared_ptr< alog_type > a, lib::shared_ptr< elog_type > e)
Initialize logging.
Definition endpoint.hpp:171
endpoint type
Type of this endpoint transport component.
Definition endpoint.hpp:49
void register_ostream(std::ostream *o)
Register a default output stream.
Definition endpoint.hpp:80
config::concurrency_type concurrency_type
Type of this endpoint's concurrency policy.
Definition endpoint.hpp:54
void set_secure(bool value)
Set whether or not endpoint can create secure connections.
Definition endpoint.hpp:102
config::alog_type alog_type
Type of this endpoint's access logging policy.
Definition endpoint.hpp:58
iostream transport error category
Definition base.hpp:85
std::string get_query() const
Return the query portion.
Definition uri.hpp:737
bool is_ipv6_literal() const
Definition uri.hpp:652
Provides streaming UTF8 validation functionality.
bool decode(iterator_type begin, iterator_type end)
Advance validator state with input from an iterator pair.
validator()
Construct and initialize the validator.
bool complete()
Return whether the input sequence ended on a valid utf8 codepoint.
void reset()
Reset the validator to decode another message.
bool consume(uint8_t byte)
Advance the state of the validator with the next input byte.
#define _WEBSOCKETPP_CONSTEXPR_TOKEN_
Definition cpp11.hpp:134
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_
Definition cpp11.hpp:115
#define __has_extension
Definition cpp11.hpp:40
#define __has_feature(x)
Definition cpp11.hpp:37
A package of types and methods for manipulating WebSocket close status'.
Definition close.hpp:47
static value const service_restart
Definition close.hpp:138
static value const message_too_big
An endpoint received a message too large to process.
Definition close.hpp:121
static value const try_again_later
Definition close.hpp:143
static value const force_tcp_drop
Close the connection with a forced TCP drop.
Definition close.hpp:72
static value const tls_handshake
An endpoint failed to perform a TLS handshake.
Definition close.hpp:157
static value const rsv_end
Last value in range reserved for future protocol use.
Definition close.hpp:177
static value const invalid_payload
An endpoint received message data inconsistent with its type.
Definition close.hpp:110
bool terminal(value code)
Determine if the code represents an unrecoverable error.
Definition close.hpp:217
static value const protocol_error
A protocol error occurred.
Definition close.hpp:83
std::string get_string(value code)
Return a human readable interpretation of a WebSocket close code.
Definition close.hpp:232
static value const bad_gateway
Definition close.hpp:148
static value const invalid_subprotocol_data
A invalid subprotocol data.
Definition close.hpp:172
static value const invalid_high
Last value in range that is always invalid on the wire.
Definition close.hpp:192
static value const unsupported_data
Definition close.hpp:91
static value const no_status
A dummy value to indicate that no status code was received.
Definition close.hpp:97
static value const omit_handshake
Close the connection without a WebSocket close handshake.
Definition close.hpp:62
static value const normal
Definition close.hpp:76
static value const blank
A blank value for internal use.
Definition close.hpp:52
static value const invalid_low
First value in range that is always invalid on the wire.
Definition close.hpp:190
static value const abnormal_close
A dummy value to indicate that the connection was closed abnormally.
Definition close.hpp:104
static value const rsv_start
First value in range reserved for future protocol use.
Definition close.hpp:175
uint16_t value
The type of a close code value.
Definition close.hpp:49
static value const policy_violation
An endpoint received a message that violated its policy.
Definition close.hpp:118
static value const extension_required
A client expected the server to accept a required extension request.
Definition close.hpp:129
bool invalid(value code)
Test whether a close code is invalid on the wire.
Definition close.hpp:199
static value const internal_endpoint_error
Definition close.hpp:133
static value const subprotocol_error
A generic subprotocol error.
Definition close.hpp:165
bool reserved(value code)
Test whether a close code is in a reserved range.
Definition close.hpp:184
static value const going_away
Definition close.hpp:80
A package of types and methods for manipulating WebSocket close codes.
Definition close.hpp:45
Concurrency handling support.
Definition basic.hpp:34
Library level error codes.
Definition error.hpp:44
@ general
Catch-all library error.
Definition error.hpp:47
@ unrequested_subprotocol
Selected subprotocol was not requested by the client.
Definition error.hpp:102
@ invalid_port
Invalid port in URI.
Definition error.hpp:120
@ client_only
Attempted to use a client specific feature on a server endpoint.
Definition error.hpp:105
@ http_connection_ended
HTTP connection ended.
Definition error.hpp:111
@ operation_canceled
The requested operation was canceled.
Definition error.hpp:127
@ no_outgoing_buffers
The endpoint is out of outgoing message buffers.
Definition error.hpp:68
@ http_parse_error
HTTP parse error.
Definition error.hpp:143
@ reserved_close_code
Close code is in a reserved range.
Definition error.hpp:80
@ con_creation_failed
Connection creation attempted failed.
Definition error.hpp:99
@ no_incoming_buffers
The endpoint is out of incoming message buffers.
Definition error.hpp:71
@ invalid_state
The connection was in the wrong state for this operation.
Definition error.hpp:74
@ extension_neg_failed
Extension negotiation failed.
Definition error.hpp:146
@ rejected
Connection rejected.
Definition error.hpp:130
@ unsupported_version
Unsupported WebSocket protocol version.
Definition error.hpp:140
@ invalid_utf8
Invalid UTF-8.
Definition error.hpp:86
@ invalid_close_code
Close code is invalid.
Definition error.hpp:83
@ server_only
Attempted to use a server specific feature on a client endpoint.
Definition error.hpp:108
@ endpoint_not_secure
Attempted to open a secure connection with an insecure endpoint.
Definition error.hpp:57
@ close_handshake_timeout
WebSocket close handshake timed out.
Definition error.hpp:117
@ invalid_subprotocol
Invalid subprotocol.
Definition error.hpp:89
@ bad_close_code
Unable to parse close code.
Definition error.hpp:77
@ open_handshake_timeout
WebSocket opening handshake timed out.
Definition error.hpp:114
@ invalid_version
Invalid WebSocket protocol version.
Definition error.hpp:137
@ transport_error
General transport error, consult more specific transport error code.
Definition error.hpp:149
@ send_queue_full
send attempted when endpoint write queue was full
Definition error.hpp:50
@ test
Unit testing utility error code.
Definition error.hpp:96
@ invalid_uri
An invalid uri was supplied.
Definition error.hpp:65
Implementation of RFC 7692, the permessage-deflate WebSocket extension.
Definition disabled.hpp:44
Constants related to frame and payload limits.
Definition frame.hpp:145
static uint8_t const close_reason_size
Maximum size of close frame reason.
Definition frame.hpp:169
static uint64_t const payload_size_jumbo
Maximum size of a jumbo WebSocket payload (basic payload = 127).
Definition frame.hpp:162
static unsigned int const max_extended_header_length
Maximum length of the variable portion of the WebSocket header.
Definition frame.hpp:153
static unsigned int const max_header_length
Maximum length of a WebSocket header.
Definition frame.hpp:150
static uint16_t const payload_size_extended
Maximum size of an extended WebSocket payload (basic payload = 126).
Definition frame.hpp:159
static uint8_t const payload_size_basic
Maximum size of a basic WebSocket payload.
Definition frame.hpp:156
static unsigned int const basic_header_length
Minimum length of a WebSocket frame header.
Definition frame.hpp:147
Constants and utility functions related to WebSocket opcodes.
Definition frame.hpp:76
bool invalid(value v)
Check if an opcode is invalid.
Definition frame.hpp:130
bool reserved(value v)
Check if an opcode is reserved.
Definition frame.hpp:118
bool is_control(value v)
Check if an opcode is for a control frame.
Definition frame.hpp:139
Data structures and utility functions for manipulating WebSocket frames.
Definition frame.hpp:45
unsigned int get_masking_key_offset(basic_header const &)
Calculate the offset location of the masking key within the extended header.
Definition frame.hpp:469
void set_rsv2(basic_header &h, bool value)
Set the frame's RSV2 bit.
Definition frame.hpp:366
static unsigned int const MAX_HEADER_LENGTH
Maximum length of a WebSocket header.
Definition frame.hpp:50
opcode::value get_opcode(basic_header const &h)
Extract opcode from basic header.
Definition frame.hpp:393
void set_rsv3(basic_header &h, bool value)
Set the frame's RSV3 bit.
Definition frame.hpp:384
uint64_t get_payload_size(basic_header const &, extended_header const &)
Extract the full payload size field from a WebSocket header.
Definition frame.hpp:573
uint8_t get_basic_size(basic_header const &)
Extracts the raw payload length specified in the basic header.
Definition frame.hpp:431
size_t byte_mask_circ(uint8_t *input, uint8_t *output, size_t length, size_t prepared_key)
Circular byte aligned mask/unmask.
Definition frame.hpp:830
void byte_mask(input_iter b, input_iter e, output_iter o, masking_key_type const &key, size_t key_offset=0)
Byte by byte mask/unmask.
Definition frame.hpp:645
static unsigned int const MAX_EXTENDED_HEADER_LENGTH
Maximum length of the variable portion of the WebSocket header.
Definition frame.hpp:52
bool get_rsv3(basic_header const &h)
check whether the frame's RSV3 bit is set
Definition frame.hpp:375
bool get_masked(basic_header const &h)
check whether the frame is masked
Definition frame.hpp:402
bool get_rsv2(basic_header const &h)
check whether the frame's RSV2 bit is set
Definition frame.hpp:357
void byte_mask(iter_type b, iter_type e, masking_key_type const &key, size_t key_offset=0)
Byte by byte mask/unmask (in place).
Definition frame.hpp:675
uint16_t get_extended_size(extended_header const &)
Extract the extended size field from an extended header.
Definition frame.hpp:540
size_t byte_mask_circ(uint8_t *data, size_t length, size_t prepared_key)
Circular byte aligned mask/unmask (in place).
Definition frame.hpp:857
bool get_fin(basic_header const &h)
Check whether the frame's FIN bit is set.
Definition frame.hpp:321
size_t circshift_prepared_key(size_t prepared_key, size_t offset)
circularly shifts the supplied prepared masking key by offset bytes
Definition frame.hpp:612
bool get_rsv1(basic_header const &h)
check whether the frame's RSV1 bit is set
Definition frame.hpp:339
void set_masked(basic_header &h, bool value)
Set the frame's MASK bit.
Definition frame.hpp:411
size_t word_mask_circ(uint8_t *input, uint8_t *output, size_t length, size_t prepared_key)
Circular word aligned mask/unmask.
Definition frame.hpp:768
void word_mask_exact(uint8_t *data, size_t length, masking_key_type const &key)
Exact word aligned mask/unmask (in place).
Definition frame.hpp:731
void set_rsv1(basic_header &h, bool value)
Set the frame's RSV1 bit.
Definition frame.hpp:348
size_t get_header_len(basic_header const &)
Calculates the full length of the header based on the first bytes.
Definition frame.hpp:445
void set_fin(basic_header &h, bool value)
Set the frame's FIN bit.
Definition frame.hpp:330
uint64_t get_jumbo_size(extended_header const &)
Extract the jumbo size field from an extended header.
Definition frame.hpp:555
void word_mask_exact(uint8_t *input, uint8_t *output, size_t length, masking_key_type const &key)
Exact word aligned mask/unmask.
Definition frame.hpp:702
std::string prepare_header(const basic_header &h, const extended_header &e)
Generate a properly sized contiguous string that encodes a full frame header.
Definition frame.hpp:489
masking_key_type get_masking_key(basic_header const &, extended_header const &)
Extract the masking key from a frame header.
Definition frame.hpp:516
static unsigned int const BASIC_HEADER_LENGTH
Minimum length of a WebSocket frame header.
Definition frame.hpp:48
size_t word_mask_circ(uint8_t *data, size_t length, size_t prepared_key)
Circular word aligned mask/unmask (in place).
Definition frame.hpp:805
size_t prepare_masking_key(masking_key_type const &key)
Extract a masking key into a value the size of a machine word.
Definition frame.hpp:595
HTTP parser errors.
status_code::value get_status_code(error::value value)
Get the HTTP status code associated with the error.
lib::error_category const & get_category()
Get a reference to a static copy of the asio transport error category.
@ unknown_transfer_encoding
The transfer encoding is unknown.
@ istream_bad
An istream read command returned with the bad flag set.
@ invalid_format
The specified data contains illegal characters for the context.
@ body_too_large
The body value is larger than the configured maximum size.
@ incomplete_request
The request was missing some required values.
@ unsupported_transfer_encoding
The transfer encoding is not supported.
@ incomplete_status_line
The response status line was missing some required values.
@ missing_header_separator
A header line was missing a separator.
@ invalid_header_name
The header name specified contains illegal characters.
@ request_header_fields_too_large
The request headers are larger than the configured maximum size.
lib::error_code make_error_code(error::value e)
Create an error code with the given value and the asio transport category.
std::string get_string(value code)
Given a status code value, return the default status message.
value
Known values for HTTP Status codes.
HTTP handling support.
Definition request.hpp:37
size_t const max_body_size
Default Maximum size in bytes for HTTP message bodies.
Definition constants.hpp:71
static char const header_separator[]
Literal value of the HTTP header separator.
Definition constants.hpp:62
std::vector< std::pair< std::string, attribute_list > > parameter_list
The type of an HTTP parameter list.
Definition constants.hpp:56
size_t const istream_buffer
Number of bytes to use for temporary istream read buffers.
Definition constants.hpp:74
bool is_not_token_char(unsigned char c)
Is the character a non-token.
size_t const max_header_size
Maximum size in bytes before rejecting an HTTP header as too big.
Definition constants.hpp:68
static char const header_delimiter[]
Literal value of the HTTP header delimiter.
Definition constants.hpp:59
bool is_whitespace_char(unsigned char c)
Is the character whitespace.
static char const header_token[]
invalid HTTP token characters
Definition constants.hpp:81
bool is_not_whitespace_char(unsigned char c)
Is the character non-whitespace.
std::map< std::string, std::string > attribute_list
The type of an HTTP attribute list.
Definition constants.hpp:48
bool is_token_char(unsigned char c)
Is the character a token.
static std::string const empty_header
Literal value of an empty header.
Definition constants.hpp:65
Constants related to processing WebSocket connections.
Definition base.hpp:44
Processor class related error codes.
Definition base.hpp:54
Error code category and codes used by all processor types.
Definition base.hpp:65
lib::error_category const & get_processor_category()
Get a reference to a static copy of the processor error category.
Definition base.hpp:238
close::status::value to_ws(lib::error_code ec)
Converts a processor error_code into a websocket close code.
Definition base.hpp:261
@ missing_required_header
Missing Required Header.
Definition base.hpp:129
@ control_too_big
Control frame too large.
Definition base.hpp:90
@ invalid_http_method
Invalid HTTP method.
Definition base.hpp:120
@ invalid_rsv_bit
Illegal use of reserved bit.
Definition base.hpp:93
@ reason_requires_code
Using a reason requires a close code.
Definition base.hpp:144
@ invalid_close_code
Invalid close code used.
Definition base.hpp:141
@ masking_required
Clients may not send unmasked frames.
Definition base.hpp:102
@ protocol_violation
Processor encountered a protocol violation in an incoming message.
Definition base.hpp:75
@ extensions_disabled
Extension related operation was ignored because extensions are disabled.
Definition base.hpp:153
@ reserved_close_code
Reserved close code used.
Definition base.hpp:138
@ extension_parse_error
Error parsing extensions.
Definition base.hpp:150
@ masking_forbidden
Servers may not send masked frames.
Definition base.hpp:105
@ not_implemented
Operation required not implemented functionality.
Definition base.hpp:117
@ invalid_opcode
Opcode was invalid for requested operation.
Definition base.hpp:87
@ sha1_library
Embedded SHA-1 library error.
Definition base.hpp:132
@ requires_64bit
Not supported on 32 bit systems.
Definition base.hpp:111
@ subprotocol_parse_error
Error parsing subprotocols.
Definition base.hpp:147
@ bad_request
Error was the result of improperly formatted user input.
Definition base.hpp:72
@ invalid_utf8
Invalid UTF-8 encoding.
Definition base.hpp:114
@ invalid_http_status
Invalid HTTP status.
Definition base.hpp:126
@ invalid_payload
Processor encountered invalid payload data.
Definition base.hpp:81
@ invalid_http_version
Invalid HTTP version.
Definition base.hpp:123
@ message_too_big
Processor encountered a message that was too large.
Definition base.hpp:78
@ no_protocol_support
No support for this feature in this protocol version.
Definition base.hpp:135
@ invalid_arguments
The processor method was called with invalid arguments.
Definition base.hpp:84
@ invalid_continuation
Continuation without message.
Definition base.hpp:99
@ non_minimal_encoding
Payload length not minimally encoded.
Definition base.hpp:108
@ fragmented_control
Fragmented control message.
Definition base.hpp:96
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
Processors encapsulate the protocol rules specific to each WebSocket version.
Definition processor.hpp:59
int get_websocket_version(request_type &r)
Extract the version from a WebSocket handshake request.
bool is_websocket_handshake(request_type &r)
Determine whether or not a generic HTTP request is a WebSocket handshake.
Definition processor.hpp:68
Stub RNG policy that always returns 0.
Definition none.hpp:35
Random number generation policies.
lib::function< void(connection_hdl, lib::asio::ip::tcp::socket &)> socket_init_handler
The signature of the socket init handler for this socket policy.
Definition none.hpp:51
Asio transport errors.
Definition base.hpp:161
lib::error_category const & get_category()
Get a reference to a static copy of the asio transport error category.
Definition base.hpp:211
lib::error_code make_error_code(error::value e)
Create an error code with the given value and the asio transport category.
Definition base.hpp:217
@ proxy_invalid
Invalid Proxy URI.
Definition base.hpp:177
@ pass_through
there was an error in the underlying transport library
Definition base.hpp:171
@ proxy_failed
The connection to the requested proxy server failed.
Definition base.hpp:174
@ invalid_num_bytes
async_read_at_least call requested more bytes than buffer can store
Definition base.hpp:168
@ invalid_host_service
Invalid host or service.
Definition base.hpp:180
Errors related to asio transport sockets.
Definition base.hpp:75
@ missing_tls_init_handler
Required tls_init handler not present.
Definition base.hpp:99
@ invalid_state
A function was called in a state that it was illegal to do so.
Definition base.hpp:86
@ tls_failed_sni_hostname
Failed to set TLS SNI hostname.
Definition base.hpp:105
@ tls_handshake_failed
TLS Handshake Failed.
Definition base.hpp:102
@ tls_handshake_timeout
TLS Handshake Timeout.
Definition base.hpp:93
@ pass_through
pass_through from underlying library
Definition base.hpp:96
Transport policy that uses asio.
Definition endpoint.hpp:46
Generic transport related errors.
@ pass_through
underlying transport pass through
@ operation_not_supported
Operation not supported.
@ operation_aborted
Operation aborted.
@ invalid_num_bytes
async_read_at_least call requested more bytes than buffer can store
@ action_after_shutdown
read or write after shutdown
@ tls_short_read
TLS short read.
@ double_read
async_read called while another async_read was in progress
iostream transport errors
Definition base.hpp:64
@ invalid_num_bytes
async_read_at_least call requested more bytes than buffer can store
Definition base.hpp:71
@ double_read
async_read called while another async_read was in progress
Definition base.hpp:74
lib::error_code make_error_code(error::value e)
Get an error code with the given value and the iostream transport category.
Definition base.hpp:118
lib::error_category const & get_category()
Get a reference to a static copy of the iostream transport error category.
Definition base.hpp:112
Transport policy that uses STL iostream for I/O and does not support timers.
Definition endpoint.hpp:43
lib::function< lib::error_code(connection_hdl, std::vector< transport::buffer > const &bufs)> vector_write_handler
Definition base.hpp:57
lib::function< lib::error_code(connection_hdl)> shutdown_handler
Definition base.hpp:61
lib::function< lib::error_code(connection_hdl, char const *, size_t)> write_handler
The type and signature of the callback used by iostream transport to write.
Definition base.hpp:48
Transport policies provide network connectivity and timers.
Definition endpoint.hpp:45
lib::function< void(lib::error_code const &, size_t)> read_handler
The type and signature of the callback passed to the read method.
lib::function< void()> dispatch_handler
The type and signature of the callback passed to the dispatch method.
lib::function< void()> interrupt_handler
The type and signature of the callback passed to the interrupt method.
lib::function< void(lib::error_code const &)> accept_handler
The type and signature of the callback passed to the accept method.
Definition endpoint.hpp:80
lib::function< void(lib::error_code const &)> timer_handler
The type and signature of the callback passed to the read method.
lib::function< void(lib::error_code const &)> connect_handler
The type and signature of the callback passed to the connect method.
Definition endpoint.hpp:83
lib::function< void(lib::error_code const &)> write_handler
The type and signature of the callback passed to the write method.
lib::function< void(lib::error_code const &)> init_handler
The type and signature of the callback passed to the init hook.
lib::function< void(lib::error_code const &)> shutdown_handler
The type and signature of the callback passed to the shutdown method.
A group of helper methods for parsing and validating URIs against RFC 3986.
Definition uri.hpp:53
bool digit(char c)
RFC3986 digit character test.
Definition uri.hpp:185
bool gen_delim(char c)
RFC3986 generic delimiter character test.
Definition uri.hpp:79
bool digit(std::string::const_iterator it)
RFC3986 digit character test (iterator version).
Definition uri.hpp:196
bool ipv4_literal(std::string::const_iterator start, std::string::const_iterator end)
Tests a range for a valid IPv4 literal.
Definition uri.hpp:251
bool sub_delim(char c)
RFC3986 subcomponent delimiter character test.
Definition uri.hpp:101
bool reg_name(std::string::const_iterator start, std::string::const_iterator end)
Tests a range for validity for a registry name.
Definition uri.hpp:374
bool pct_encoded(std::string::const_iterator it)
RFC3986 per cent encoded character test.
Definition uri.hpp:212
bool scheme(char c)
RFC3986 scheme character test.
Definition uri.hpp:166
bool unreserved(char c)
RFC3986 unreserved character test.
Definition uri.hpp:62
bool reg_name(char c)
Tests a character for validity for a registry name.
Definition uri.hpp:362
bool hex4(std::string::const_iterator start, std::string::const_iterator end)
Tests a range for a valid IPv6 hex quad.
Definition uri.hpp:280
bool dec_octet(std::string::const_iterator start, std::string::const_iterator end)
Tests a range for a valid IPv4 decimal octet.
Definition uri.hpp:224
bool ipv6_literal(std::string::const_iterator start, std::string::const_iterator end)
Tests a range for a valid IPv6 literal.
Definition uri.hpp:300
bool hexdigit(char c)
RFC3986 hex digit character test.
Definition uri.hpp:129
Generic non-websocket specific utility functions and data structures.
Definition utilities.hpp:39
std::string to_hex(uint8_t const *input, size_t length)
Convert byte array (uint8_t) to ascii printed string of hex digits.
T::const_iterator ci_find_substr(T const &haystack, T const &needle, std::locale const &loc=std::locale())
Find substring (case insensitive).
T::const_iterator ci_find_substr(T const &haystack, typename T::value_type const *needle, typename T::size_type size, std::locale const &loc=std::locale())
Find substring (case insensitive).
std::string to_hex(char const *input, size_t length)
Convert char array to ascii printed string of hex digits.
Namespace for the WebSocket++ project.
lib::function< void(connection_hdl, std::string)> pong_handler
The type and function signature of a pong handler.
lib::function< void(connection_hdl)> close_handler
The type and function signature of a close handler.
lib::function< void(connection_hdl, std::string)> pong_timeout_handler
The type and function signature of a pong timeout handler.
lib::function< void(connection_hdl)> http_handler
The type and function signature of a http handler.
lib::function< void(connection_hdl)> open_handler
The type and function signature of an open handler.
lib::function< void(connection_hdl)> interrupt_handler
The type and function signature of an interrupt handler.
static uint16_t const uri_default_secure_port
Default port for wss://.
Definition uri.hpp:48
lib::function< void(connection_hdl)> fail_handler
The type and function signature of a fail handler.
lib::weak_ptr< void > connection_hdl
A handle to uniquely identify a connection.
static uint16_t const uri_default_port
Default port for ws://.
Definition uri.hpp:46
lib::function< bool(connection_hdl, std::string)> ping_handler
The type and function signature of a ping handler.
static char const user_agent[]
Default user agent string.
Definition version.hpp:57
lib::shared_ptr< uri > uri_ptr
Pointer to a URI.
Definition uri.hpp:796
std::pair< lib::error_code, std::string > err_str_pair
Combination error code / string type for returning two values.
Definition error.hpp:41
lib::function< bool(connection_hdl)> validate_handler
The type and function signature of a validate handler.
static bool is_base64(unsigned char c)
Test whether a character is a valid base64 character.
Definition base64.hpp:53
std::string base64_encode(unsigned char const *input, size_t len)
Encode a char buffer into a base64 string.
Definition base64.hpp:66
Server config with asio transport and TLS disabled.
static const bool allow_disabling_context_takeover
Maximum size (in bytes) of a decompressed message.
Definition core.hpp:275
static const long timeout_socket_shutdown
Length of time to wait for socket shutdown.
Definition core.hpp:137
static const long timeout_connect
Length of time to wait for TCP connect.
Definition core.hpp:134
static const long timeout_dns_resolve
Length of time to wait for dns resolution.
Definition core.hpp:131
static const long timeout_proxy
Length of time to wait before a proxy handshake is aborted.
Definition core.hpp:121
static const long timeout_socket_pre_init
Default timer values (in ms).
Definition core.hpp:118
static const long timeout_socket_post_init
Length of time to wait for socket post-initialization.
Definition core.hpp:128
Server config with iostream transport.
Definition core.hpp:68
websocketpp::random::none::int_generator< uint32_t > rng_type
RNG policies.
Definition core.hpp:93
static const websocketpp::log::level elog_level
Default static error logging channels.
Definition core.hpp:176
websocketpp::transport::iostream::endpoint< transport_config > transport_type
Transport Endpoint Component.
Definition core.hpp:142
static const size_t max_http_body_size
Default maximum http body size.
Definition core.hpp:252
static const long timeout_open_handshake
Default timer values (in ms).
Definition core.hpp:152
static const size_t max_message_size
Default maximum message size.
Definition core.hpp:240
static const bool drop_on_protocol_error
Drop connections immediately on protocol error.
Definition core.hpp:213
static const long timeout_close_handshake
Length of time before a closing handshake is aborted.
Definition core.hpp:154
static const websocketpp::log::level alog_level
Default static access logging channels.
Definition core.hpp:189
websocketpp::log::basic< concurrency_type, websocketpp::log::elevel > elog_type
Logging policies.
Definition core.hpp:88
static const long timeout_pong
Length of time to wait for a pong after a ping.
Definition core.hpp:156
static const bool silent_close
Suppresses the return of detailed connection close information.
Definition core.hpp:228
static bool const enable_multithreading
Definition core.hpp:98
static const size_t connection_read_buffer_size
Size of the per-connection read buffer.
Definition core.hpp:204
static const bool enable_extensions
Global flag for enabling/disabling extensions.
Definition core.hpp:255
static const int client_version
WebSocket Protocol version to use as a client.
Definition core.hpp:164
The constant size component of a WebSocket frame header.
Definition frame.hpp:189
The variable size component of a WebSocket frame header.
Definition frame.hpp:235
Package of log levels for logging access events.
Definition levels.hpp:112
static char const * channel_name(level channel)
Get the textual name of a channel given a channel id.
Definition levels.hpp:164
static level const fail
One line for each failed WebSocket connection with details.
Definition levels.hpp:147
static level const none
Special aggregate value representing "no levels".
Definition levels.hpp:114
static level const debug_handshake
Extra information about opening handshakes.
Definition levels.hpp:137
static level const devel
Development messages (warning: very chatty).
Definition levels.hpp:141
static level const all
Special aggregate value representing "all levels".
Definition levels.hpp:152
static level const debug_close
Extra information about closing handshakes.
Definition levels.hpp:139
static level const frame_payload
One line per frame, includes the full message payload (warning: chatty).
Definition levels.hpp:129
static level const connect
Information about new connections.
Definition levels.hpp:121
static level const app
Special channel for application specific logs. Not used by the library.
Definition levels.hpp:143
static level const frame_header
One line per frame, includes the full frame header.
Definition levels.hpp:127
static level const message_payload
Reserved.
Definition levels.hpp:133
static level const endpoint
Reserved.
Definition levels.hpp:135
static level const message_header
Reserved.
Definition levels.hpp:131
static level const control
One line per control frame.
Definition levels.hpp:125
static level const disconnect
One line for each closed connection. Includes closing codes and reasons.
Definition levels.hpp:123
static level const access_core
Definition levels.hpp:150
static level const http
Access related to HTTP requests.
Definition levels.hpp:145
Package of values for hinting at the nature of a given logger.
Definition levels.hpp:46
static value const none
No information.
Definition levels.hpp:51
static value const access
Access log.
Definition levels.hpp:53
static value const error
Error log.
Definition levels.hpp:55
uint32_t value
Type of a channel type hint value.
Definition levels.hpp:48
Package of log levels for logging errors.
Definition levels.hpp:59
static level const devel
Low level debugging information (warning: very chatty).
Definition levels.hpp:63
static char const * channel_name(level channel)
Get the textual name of a channel given a channel id.
Definition levels.hpp:91
static level const library
Definition levels.hpp:66
static level const info
Definition levels.hpp:69
static level const all
Special aggregate value representing "all levels".
Definition levels.hpp:80
static level const fatal
Definition levels.hpp:78
static level const none
Special aggregate value representing "no levels".
Definition levels.hpp:61
static level const rerror
Definition levels.hpp:75
static level const warn
Definition levels.hpp:72
A simple utility buffer class.
Helper less than functor for case insensitive find.
Definition utilities.hpp:75
Helper functor for case insensitive find.
Definition utilities.hpp:49
bool operator()(charT ch1, charT ch2)
Perform a case insensitive comparison.
Definition utilities.hpp:63
my_equal(std::locale const &loc)
Construct the functor with the given locale.
Definition utilities.hpp:54
#define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
#define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
Type used to convert close statuses between integer and wire representations.
Definition close.hpp:275
Two byte conversion union.
Definition frame.hpp:55
Four byte conversion union.
Definition frame.hpp:61
Eight byte conversion union.
Definition frame.hpp:67