From ca3c508a028ff0b85d83619e320c8ab47bd9def0 Mon Sep 17 00:00:00 2001 From: aMannus Date: Tue, 6 May 2025 13:17:14 +0200 Subject: [PATCH] apclientpp first attempt at dependencies --- .gitmodules | 6 + soh/CMakeLists.txt | 17 + soh/soh/Network/Archipelago/Archipelago.cpp | 2 + soh/soh/Network/Archipelago/Archipelago.h | 1 + subprojects/apclientpp | 1 + subprojects/asio/COPYING | 4 + subprojects/asio/LICENSE_1_0.txt | 23 + subprojects/asio/include/asio.hpp | 152 + subprojects/asio/include/asio/COPYING | 4 + subprojects/asio/include/asio/LICENSE_1_0.txt | 23 + .../include/asio/associated_allocator.hpp | 131 + .../asio/include/asio/associated_executor.hpp | 149 + .../asio/include/asio/async_result.hpp | 221 + .../include/asio/basic_datagram_socket.hpp | 1040 +++++ .../include/asio/basic_deadline_timer.hpp | 628 +++ .../asio/include/asio/basic_io_object.hpp | 290 ++ .../asio/include/asio/basic_raw_socket.hpp | 1030 +++++ .../include/asio/basic_seq_packet_socket.hpp | 618 +++ .../asio/include/asio/basic_serial_port.hpp | 688 ++++ .../asio/include/asio/basic_signal_set.hpp | 391 ++ .../asio/include/asio/basic_socket.hpp | 1757 ++++++++ .../include/asio/basic_socket_acceptor.hpp | 1986 +++++++++ .../include/asio/basic_socket_iostream.hpp | 430 ++ .../include/asio/basic_socket_streambuf.hpp | 707 ++++ .../asio/include/asio/basic_stream_socket.hpp | 921 +++++ .../asio/include/asio/basic_streambuf.hpp | 452 +++ .../asio/include/asio/basic_streambuf_fwd.hpp | 36 + .../include/asio/basic_waitable_timer.hpp | 705 ++++ .../asio/include/asio/bind_executor.hpp | 611 +++ subprojects/asio/include/asio/buffer.hpp | 2169 ++++++++++ .../include/asio/buffered_read_stream.hpp | 257 ++ .../include/asio/buffered_read_stream_fwd.hpp | 25 + .../asio/include/asio/buffered_stream.hpp | 278 ++ .../asio/include/asio/buffered_stream_fwd.hpp | 25 + .../include/asio/buffered_write_stream.hpp | 249 ++ .../asio/buffered_write_stream_fwd.hpp | 25 + .../asio/include/asio/buffers_iterator.hpp | 521 +++ .../include/asio/completion_condition.hpp | 218 + subprojects/asio/include/asio/connect.hpp | 1060 +++++ subprojects/asio/include/asio/coroutine.hpp | 328 ++ .../include/asio/datagram_socket_service.hpp | 466 +++ .../asio/include/asio/deadline_timer.hpp | 38 + .../include/asio/deadline_timer_service.hpp | 173 + subprojects/asio/include/asio/defer.hpp | 107 + .../asio/include/asio/detail/array.hpp | 38 + .../asio/include/asio/detail/array_fwd.hpp | 34 + .../asio/include/asio/detail/assert.hpp | 32 + .../asio/include/asio/detail/atomic_count.hpp | 45 + .../asio/detail/base_from_completion_cond.hpp | 68 + .../asio/include/asio/detail/bind_handler.hpp | 816 ++++ .../asio/detail/buffer_resize_guard.hpp | 66 + .../asio/detail/buffer_sequence_adapter.hpp | 544 +++ .../asio/detail/buffered_stream_storage.hpp | 126 + .../asio/include/asio/detail/call_stack.hpp | 125 + .../asio/include/asio/detail/chrono.hpp | 66 + .../asio/detail/chrono_time_traits.hpp | 190 + .../asio/detail/completion_handler.hpp | 83 + .../include/asio/detail/concurrency_hint.hpp | 94 + .../detail/conditionally_enabled_event.hpp | 112 + .../detail/conditionally_enabled_mutex.hpp | 149 + .../asio/include/asio/detail/config.hpp | 1456 +++++++ .../include/asio/detail/consuming_buffers.hpp | 414 ++ .../asio/include/asio/detail/cstddef.hpp | 31 + .../asio/include/asio/detail/cstdint.hpp | 60 + .../include/asio/detail/date_time_fwd.hpp | 34 + .../asio/detail/deadline_timer_service.hpp | 278 ++ .../include/asio/detail/dependent_type.hpp | 36 + .../include/asio/detail/descriptor_ops.hpp | 121 + .../asio/detail/descriptor_read_op.hpp | 128 + .../asio/detail/descriptor_write_op.hpp | 128 + .../include/asio/detail/dev_poll_reactor.hpp | 218 + .../include/asio/detail/epoll_reactor.hpp | 266 ++ .../asio/include/asio/detail/event.hpp | 48 + .../detail/eventfd_select_interrupter.hpp | 83 + .../asio/include/asio/detail/executor_op.hpp | 84 + .../include/asio/detail/fd_set_adapter.hpp | 39 + .../asio/include/asio/detail/fenced_block.hpp | 80 + .../asio/include/asio/detail/functional.hpp | 38 + .../asio/include/asio/detail/future.hpp | 33 + .../asio/detail/gcc_arm_fenced_block.hpp | 91 + .../asio/detail/gcc_hppa_fenced_block.hpp | 68 + .../asio/detail/gcc_sync_fenced_block.hpp | 65 + .../asio/detail/gcc_x86_fenced_block.hpp | 99 + .../asio/include/asio/detail/global.hpp | 52 + .../asio/detail/handler_alloc_helpers.hpp | 235 ++ .../asio/detail/handler_cont_helpers.hpp | 45 + .../asio/detail/handler_invoke_helpers.hpp | 57 + .../include/asio/detail/handler_tracking.hpp | 238 ++ .../asio/detail/handler_type_requirements.hpp | 556 +++ .../asio/include/asio/detail/handler_work.hpp | 95 + .../asio/include/asio/detail/hash_map.hpp | 331 ++ .../detail/impl/buffer_sequence_adapter.ipp | 118 + .../asio/detail/impl/descriptor_ops.ipp | 474 +++ .../asio/detail/impl/dev_poll_reactor.hpp | 91 + .../asio/detail/impl/dev_poll_reactor.ipp | 446 ++ .../asio/detail/impl/epoll_reactor.hpp | 89 + .../asio/detail/impl/epoll_reactor.ipp | 787 ++++ .../impl/eventfd_select_interrupter.ipp | 165 + .../asio/detail/impl/handler_tracking.ipp | 358 ++ .../asio/detail/impl/kqueue_reactor.hpp | 93 + .../asio/detail/impl/kqueue_reactor.ipp | 566 +++ .../include/asio/detail/impl/null_event.ipp | 74 + .../detail/impl/pipe_select_interrupter.ipp | 124 + .../include/asio/detail/impl/posix_event.ipp | 59 + .../include/asio/detail/impl/posix_mutex.ipp | 46 + .../include/asio/detail/impl/posix_thread.ipp | 84 + .../asio/detail/impl/posix_tss_ptr.ipp | 46 + .../impl/reactive_descriptor_service.ipp | 222 + .../impl/reactive_serial_port_service.ipp | 152 + .../impl/reactive_socket_service_base.ipp | 300 ++ .../detail/impl/resolver_service_base.ipp | 154 + .../include/asio/detail/impl/scheduler.ipp | 571 +++ .../asio/detail/impl/select_reactor.hpp | 100 + .../asio/detail/impl/select_reactor.ipp | 333 ++ .../asio/detail/impl/service_registry.hpp | 94 + .../asio/detail/impl/service_registry.ipp | 197 + .../asio/detail/impl/signal_set_service.ipp | 669 +++ .../include/asio/detail/impl/socket_ops.ipp | 3571 +++++++++++++++++ .../detail/impl/socket_select_interrupter.ipp | 176 + .../detail/impl/strand_executor_service.hpp | 179 + .../detail/impl/strand_executor_service.ipp | 134 + .../asio/detail/impl/strand_service.hpp | 118 + .../asio/detail/impl/strand_service.ipp | 177 + .../include/asio/detail/impl/throw_error.ipp | 60 + .../asio/detail/impl/timer_queue_ptime.ipp | 91 + .../asio/detail/impl/timer_queue_set.ipp | 101 + .../include/asio/detail/impl/win_event.ipp | 76 + .../detail/impl/win_iocp_handle_service.ipp | 525 +++ .../asio/detail/impl/win_iocp_io_context.hpp | 103 + .../asio/detail/impl/win_iocp_io_context.ipp | 554 +++ .../impl/win_iocp_serial_port_service.ipp | 181 + .../impl/win_iocp_socket_service_base.ipp | 799 ++++ .../include/asio/detail/impl/win_mutex.ipp | 84 + .../detail/impl/win_object_handle_service.ipp | 449 +++ .../asio/detail/impl/win_static_mutex.ipp | 136 + .../include/asio/detail/impl/win_thread.ipp | 150 + .../include/asio/detail/impl/win_tss_ptr.ipp | 57 + .../impl/winrt_ssocket_service_base.ipp | 629 +++ .../detail/impl/winrt_timer_scheduler.hpp | 92 + .../detail/impl/winrt_timer_scheduler.ipp | 122 + .../include/asio/detail/impl/winsock_init.ipp | 82 + .../asio/include/asio/detail/io_control.hpp | 84 + .../asio/detail/is_buffer_sequence.hpp | 263 ++ .../asio/include/asio/detail/is_executor.hpp | 126 + .../include/asio/detail/keyword_tss_ptr.hpp | 70 + .../include/asio/detail/kqueue_reactor.hpp | 242 ++ .../asio/include/asio/detail/limits.hpp | 26 + .../asio/detail/local_free_on_block_exit.hpp | 59 + .../asio/detail/macos_fenced_block.hpp | 62 + .../asio/include/asio/detail/memory.hpp | 70 + .../asio/include/asio/detail/mutex.hpp | 48 + .../asio/include/asio/detail/noncopyable.hpp | 43 + .../asio/include/asio/detail/null_event.hpp | 100 + .../include/asio/detail/null_fenced_block.hpp | 47 + .../asio/include/asio/detail/null_global.hpp | 59 + .../asio/include/asio/detail/null_mutex.hpp | 64 + .../asio/include/asio/detail/null_reactor.hpp | 68 + .../asio/detail/null_signal_blocker.hpp | 69 + .../asio/detail/null_socket_service.hpp | 508 +++ .../include/asio/detail/null_static_mutex.hpp | 60 + .../asio/include/asio/detail/null_thread.hpp | 67 + .../asio/include/asio/detail/null_tss_ptr.hpp | 68 + .../asio/include/asio/detail/object_pool.hpp | 171 + .../asio/detail/old_win_sdk_compat.hpp | 214 + .../asio/include/asio/detail/op_queue.hpp | 162 + .../asio/include/asio/detail/operation.hpp | 38 + .../asio/detail/pipe_select_interrupter.hpp | 89 + .../asio/include/asio/detail/pop_options.hpp | 135 + .../asio/include/asio/detail/posix_event.hpp | 162 + .../asio/detail/posix_fd_set_adapter.hpp | 118 + .../asio/include/asio/detail/posix_global.hpp | 80 + .../asio/include/asio/detail/posix_mutex.hpp | 76 + .../asio/detail/posix_signal_blocker.hpp | 85 + .../asio/detail/posix_static_mutex.hpp | 64 + .../asio/include/asio/detail/posix_thread.hpp | 109 + .../include/asio/detail/posix_tss_ptr.hpp | 79 + .../asio/include/asio/detail/push_options.hpp | 175 + .../detail/reactive_descriptor_service.hpp | 388 ++ .../asio/detail/reactive_null_buffers_op.hpp | 90 + .../detail/reactive_serial_port_service.hpp | 236 ++ .../asio/detail/reactive_socket_accept_op.hpp | 217 + .../detail/reactive_socket_connect_op.hpp | 113 + .../asio/detail/reactive_socket_recv_op.hpp | 135 + .../detail/reactive_socket_recvfrom_op.hpp | 138 + .../detail/reactive_socket_recvmsg_op.hpp | 132 + .../asio/detail/reactive_socket_send_op.hpp | 134 + .../asio/detail/reactive_socket_sendto_op.hpp | 130 + .../asio/detail/reactive_socket_service.hpp | 526 +++ .../detail/reactive_socket_service_base.hpp | 511 +++ .../include/asio/detail/reactive_wait_op.hpp | 90 + .../asio/include/asio/detail/reactor.hpp | 32 + .../asio/include/asio/detail/reactor_fwd.hpp | 40 + .../asio/include/asio/detail/reactor_op.hpp | 65 + .../include/asio/detail/reactor_op_queue.hpp | 168 + .../asio/detail/recycling_allocator.hpp | 104 + .../asio/include/asio/detail/regex_fwd.hpp | 35 + .../asio/detail/resolve_endpoint_op.hpp | 122 + .../asio/include/asio/detail/resolve_op.hpp | 45 + .../include/asio/detail/resolve_query_op.hpp | 134 + .../include/asio/detail/resolver_service.hpp | 145 + .../asio/detail/resolver_service_base.hpp | 140 + .../asio/include/asio/detail/scheduler.hpp | 213 + .../asio/detail/scheduler_operation.hpp | 78 + .../asio/detail/scheduler_thread_info.hpp | 40 + .../asio/include/asio/detail/scoped_lock.hpp | 101 + .../asio/include/asio/detail/scoped_ptr.hpp | 87 + .../asio/detail/select_interrupter.hpp | 46 + .../include/asio/detail/select_reactor.hpp | 238 ++ .../include/asio/detail/service_registry.hpp | 164 + .../include/asio/detail/signal_blocker.hpp | 44 + .../include/asio/detail/signal_handler.hpp | 86 + .../asio/include/asio/detail/signal_init.hpp | 47 + .../asio/include/asio/detail/signal_op.hpp | 49 + .../asio/detail/signal_set_service.hpp | 217 + .../include/asio/detail/socket_holder.hpp | 98 + .../asio/include/asio/detail/socket_ops.hpp | 337 ++ .../include/asio/detail/socket_option.hpp | 316 ++ .../asio/detail/socket_select_interrupter.hpp | 91 + .../asio/include/asio/detail/socket_types.hpp | 416 ++ .../asio/detail/solaris_fenced_block.hpp | 62 + .../asio/include/asio/detail/static_mutex.hpp | 52 + .../asio/include/asio/detail/std_event.hpp | 176 + .../include/asio/detail/std_fenced_block.hpp | 62 + .../asio/include/asio/detail/std_global.hpp | 70 + .../asio/include/asio/detail/std_mutex.hpp | 73 + .../include/asio/detail/std_static_mutex.hpp | 81 + .../asio/include/asio/detail/std_thread.hpp | 71 + .../asio/detail/strand_executor_service.hpp | 142 + .../include/asio/detail/strand_service.hpp | 142 + .../asio/include/asio/detail/string_view.hpp | 47 + .../asio/include/asio/detail/thread.hpp | 60 + .../include/asio/detail/thread_context.hpp | 42 + .../asio/include/asio/detail/thread_group.hpp | 89 + .../include/asio/detail/thread_info_base.hpp | 121 + .../asio/include/asio/detail/throw_error.hpp | 53 + .../include/asio/detail/throw_exception.hpp | 51 + .../asio/include/asio/detail/timer_queue.hpp | 360 ++ .../include/asio/detail/timer_queue_base.hpp | 68 + .../include/asio/detail/timer_queue_ptime.hpp | 99 + .../include/asio/detail/timer_queue_set.hpp | 66 + .../include/asio/detail/timer_scheduler.hpp | 35 + .../asio/detail/timer_scheduler_fwd.hpp | 40 + .../asio/include/asio/detail/tss_ptr.hpp | 69 + .../asio/include/asio/detail/type_traits.hpp | 86 + .../asio/detail/variadic_templates.hpp | 119 + .../asio/include/asio/detail/wait_handler.hpp | 85 + .../asio/include/asio/detail/wait_op.hpp | 45 + .../asio/include/asio/detail/win_event.hpp | 151 + .../asio/detail/win_fd_set_adapter.hpp | 149 + .../include/asio/detail/win_fenced_block.hpp | 90 + .../asio/include/asio/detail/win_global.hpp | 74 + .../asio/detail/win_iocp_handle_read_op.hpp | 111 + .../asio/detail/win_iocp_handle_service.hpp | 323 ++ .../asio/detail/win_iocp_handle_write_op.hpp | 103 + .../asio/detail/win_iocp_io_context.hpp | 328 ++ .../asio/detail/win_iocp_null_buffers_op.hpp | 121 + .../asio/detail/win_iocp_operation.hpp | 96 + .../asio/detail/win_iocp_overlapped_op.hpp | 90 + .../asio/detail/win_iocp_overlapped_ptr.hpp | 143 + .../detail/win_iocp_serial_port_service.hpp | 230 ++ .../asio/detail/win_iocp_socket_accept_op.hpp | 297 ++ .../detail/win_iocp_socket_connect_op.hpp | 127 + .../asio/detail/win_iocp_socket_recv_op.hpp | 117 + .../detail/win_iocp_socket_recvfrom_op.hpp | 125 + .../detail/win_iocp_socket_recvmsg_op.hpp | 118 + .../asio/detail/win_iocp_socket_send_op.hpp | 111 + .../asio/detail/win_iocp_socket_service.hpp | 599 +++ .../detail/win_iocp_socket_service_base.hpp | 591 +++ .../asio/detail/win_iocp_thread_info.hpp | 34 + .../include/asio/detail/win_iocp_wait_op.hpp | 121 + .../asio/include/asio/detail/win_mutex.hpp | 78 + .../asio/detail/win_object_handle_service.hpp | 184 + .../include/asio/detail/win_static_mutex.hpp | 74 + .../asio/include/asio/detail/win_thread.hpp | 147 + .../asio/include/asio/detail/win_tss_ptr.hpp | 79 + .../include/asio/detail/winapp_thread.hpp | 124 + .../asio/include/asio/detail/wince_thread.hpp | 124 + .../asio/detail/winrt_async_manager.hpp | 294 ++ .../include/asio/detail/winrt_async_op.hpp | 65 + .../include/asio/detail/winrt_resolve_op.hpp | 118 + .../asio/detail/winrt_resolver_service.hpp | 198 + .../asio/detail/winrt_socket_connect_op.hpp | 92 + .../asio/detail/winrt_socket_recv_op.hpp | 112 + .../asio/detail/winrt_socket_send_op.hpp | 103 + .../asio/detail/winrt_ssocket_service.hpp | 241 ++ .../detail/winrt_ssocket_service_base.hpp | 359 ++ .../asio/detail/winrt_timer_scheduler.hpp | 137 + .../asio/include/asio/detail/winrt_utils.hpp | 106 + .../asio/include/asio/detail/winsock_init.hpp | 128 + .../include/asio/detail/work_dispatcher.hpp | 72 + .../include/asio/detail/wrapped_handler.hpp | 291 ++ subprojects/asio/include/asio/dispatch.hpp | 108 + subprojects/asio/include/asio/error.hpp | 356 ++ subprojects/asio/include/asio/error_code.hpp | 202 + .../asio/include/asio/execution_context.hpp | 411 ++ subprojects/asio/include/asio/executor.hpp | 341 ++ .../asio/include/asio/executor_work_guard.hpp | 170 + .../asio/include/asio/experimental.hpp | 22 + .../include/asio/experimental/co_spawn.hpp | 226 ++ .../include/asio/experimental/detached.hpp | 65 + .../asio/experimental/impl/co_spawn.hpp | 876 ++++ .../asio/experimental/impl/detached.hpp | 91 + .../asio/experimental/impl/redirect_error.hpp | 294 ++ .../asio/experimental/redirect_error.hpp | 67 + .../include/asio/generic/basic_endpoint.hpp | 193 + .../asio/generic/datagram_protocol.hpp | 123 + .../include/asio/generic/detail/endpoint.hpp | 133 + .../asio/generic/detail/impl/endpoint.ipp | 110 + .../include/asio/generic/raw_protocol.hpp | 121 + .../asio/generic/seq_packet_protocol.hpp | 122 + .../include/asio/generic/stream_protocol.hpp | 127 + .../asio/include/asio/handler_alloc_hook.hpp | 81 + .../asio/handler_continuation_hook.hpp | 54 + .../asio/include/asio/handler_invoke_hook.hpp | 85 + .../asio/include/asio/handler_type.hpp | 50 + .../include/asio/high_resolution_timer.hpp | 44 + .../asio/impl/buffered_read_stream.hpp | 429 ++ .../asio/impl/buffered_write_stream.hpp | 411 ++ .../asio/include/asio/impl/connect.hpp | 860 ++++ subprojects/asio/include/asio/impl/defer.hpp | 77 + .../asio/include/asio/impl/dispatch.hpp | 78 + subprojects/asio/include/asio/impl/error.ipp | 128 + .../asio/include/asio/impl/error_code.ipp | 206 + .../include/asio/impl/execution_context.hpp | 107 + .../include/asio/impl/execution_context.ipp | 82 + .../asio/include/asio/impl/executor.hpp | 386 ++ .../asio/include/asio/impl/executor.ipp | 38 + .../include/asio/impl/handler_alloc_hook.ipp | 52 + .../asio/include/asio/impl/io_context.hpp | 343 ++ .../asio/include/asio/impl/io_context.ipp | 174 + subprojects/asio/include/asio/impl/post.hpp | 77 + subprojects/asio/include/asio/impl/read.hpp | 715 ++++ .../asio/include/asio/impl/read_at.hpp | 640 +++ .../asio/include/asio/impl/read_until.hpp | 1502 +++++++ .../include/asio/impl/serial_port_base.hpp | 59 + .../include/asio/impl/serial_port_base.ipp | 554 +++ subprojects/asio/include/asio/impl/spawn.hpp | 535 +++ subprojects/asio/include/asio/impl/src.cpp | 25 + subprojects/asio/include/asio/impl/src.hpp | 82 + .../asio/include/asio/impl/system_context.hpp | 34 + .../asio/include/asio/impl/system_context.ipp | 73 + .../include/asio/impl/system_executor.hpp | 85 + .../asio/include/asio/impl/thread_pool.hpp | 127 + .../asio/include/asio/impl/thread_pool.ipp | 76 + .../asio/include/asio/impl/use_future.hpp | 937 +++++ subprojects/asio/include/asio/impl/write.hpp | 674 ++++ .../asio/include/asio/impl/write_at.hpp | 572 +++ subprojects/asio/include/asio/io_context.hpp | 876 ++++ .../asio/include/asio/io_context_strand.hpp | 384 ++ subprojects/asio/include/asio/io_service.hpp | 33 + .../asio/include/asio/io_service_strand.hpp | 20 + subprojects/asio/include/asio/ip/address.hpp | 260 ++ .../asio/include/asio/ip/address_v4.hpp | 329 ++ .../include/asio/ip/address_v4_iterator.hpp | 162 + .../asio/include/asio/ip/address_v4_range.hpp | 134 + .../asio/include/asio/ip/address_v6.hpp | 336 ++ .../include/asio/ip/address_v6_iterator.hpp | 183 + .../asio/include/asio/ip/address_v6_range.hpp | 129 + .../asio/include/asio/ip/bad_address_cast.hpp | 53 + .../asio/include/asio/ip/basic_endpoint.hpp | 263 ++ .../asio/include/asio/ip/basic_resolver.hpp | 1020 +++++ .../include/asio/ip/basic_resolver_entry.hpp | 113 + .../asio/ip/basic_resolver_iterator.hpp | 192 + .../include/asio/ip/basic_resolver_query.hpp | 244 ++ .../asio/ip/basic_resolver_results.hpp | 311 ++ .../asio/include/asio/ip/detail/endpoint.hpp | 139 + .../include/asio/ip/detail/impl/endpoint.ipp | 199 + .../include/asio/ip/detail/socket_option.hpp | 566 +++ .../asio/include/asio/ip/host_name.hpp | 42 + subprojects/asio/include/asio/ip/icmp.hpp | 115 + .../asio/include/asio/ip/impl/address.hpp | 67 + .../asio/include/asio/ip/impl/address.ipp | 234 ++ .../asio/include/asio/ip/impl/address_v4.hpp | 67 + .../asio/include/asio/ip/impl/address_v4.ipp | 210 + .../asio/include/asio/ip/impl/address_v6.hpp | 67 + .../asio/include/asio/ip/impl/address_v6.ipp | 350 ++ .../include/asio/ip/impl/basic_endpoint.hpp | 43 + .../asio/include/asio/ip/impl/host_name.ipp | 54 + .../asio/include/asio/ip/impl/network_v4.hpp | 54 + .../asio/include/asio/ip/impl/network_v4.ipp | 216 + .../asio/include/asio/ip/impl/network_v6.hpp | 53 + .../asio/include/asio/ip/impl/network_v6.ipp | 185 + .../asio/include/asio/ip/multicast.hpp | 191 + .../asio/include/asio/ip/network_v4.hpp | 261 ++ .../asio/include/asio/ip/network_v6.hpp | 235 ++ .../asio/include/asio/ip/resolver_base.hpp | 129 + .../include/asio/ip/resolver_query_base.hpp | 43 + .../asio/include/asio/ip/resolver_service.hpp | 200 + subprojects/asio/include/asio/ip/tcp.hpp | 155 + subprojects/asio/include/asio/ip/udp.hpp | 111 + subprojects/asio/include/asio/ip/unicast.hpp | 70 + subprojects/asio/include/asio/ip/v6_only.hpp | 69 + subprojects/asio/include/asio/is_executor.hpp | 46 + .../asio/include/asio/is_read_buffered.hpp | 59 + .../asio/include/asio/is_write_buffered.hpp | 59 + .../include/asio/local/basic_endpoint.hpp | 239 ++ .../asio/include/asio/local/connect_pair.hpp | 106 + .../include/asio/local/datagram_protocol.hpp | 80 + .../include/asio/local/detail/endpoint.hpp | 133 + .../asio/local/detail/impl/endpoint.ipp | 129 + .../include/asio/local/stream_protocol.hpp | 90 + .../asio/include/asio/packaged_task.hpp | 126 + .../asio/include/asio/placeholders.hpp | 151 + .../include/asio/posix/basic_descriptor.hpp | 582 +++ .../asio/posix/basic_stream_descriptor.hpp | 362 ++ .../asio/include/asio/posix/descriptor.hpp | 644 +++ .../include/asio/posix/descriptor_base.hpp | 90 + .../include/asio/posix/stream_descriptor.hpp | 360 ++ .../asio/posix/stream_descriptor_service.hpp | 279 ++ subprojects/asio/include/asio/post.hpp | 107 + .../asio/include/asio/raw_socket_service.hpp | 466 +++ subprojects/asio/include/asio/read.hpp | 947 +++++ subprojects/asio/include/asio/read_at.hpp | 671 ++++ subprojects/asio/include/asio/read_until.hpp | 1826 +++++++++ .../asio/seq_packet_socket_service.hpp | 416 ++ subprojects/asio/include/asio/serial_port.hpp | 769 ++++ .../asio/include/asio/serial_port_base.hpp | 167 + .../asio/include/asio/serial_port_service.hpp | 249 ++ subprojects/asio/include/asio/signal_set.hpp | 447 +++ .../asio/include/asio/signal_set_service.hpp | 142 + .../include/asio/socket_acceptor_service.hpp | 372 ++ subprojects/asio/include/asio/socket_base.hpp | 559 +++ subprojects/asio/include/asio/spawn.hpp | 336 ++ subprojects/asio/include/asio/ssl.hpp | 27 + subprojects/asio/include/asio/ssl/context.hpp | 758 ++++ .../asio/include/asio/ssl/context_base.hpp | 209 + .../asio/ssl/detail/buffered_handshake_op.hpp | 114 + .../asio/include/asio/ssl/detail/engine.hpp | 160 + .../include/asio/ssl/detail/handshake_op.hpp | 62 + .../include/asio/ssl/detail/impl/engine.ipp | 322 ++ .../asio/ssl/detail/impl/openssl_init.ipp | 165 + .../asio/include/asio/ssl/detail/io.hpp | 372 ++ .../include/asio/ssl/detail/openssl_init.hpp | 101 + .../include/asio/ssl/detail/openssl_types.hpp | 30 + .../asio/ssl/detail/password_callback.hpp | 66 + .../asio/include/asio/ssl/detail/read_op.hpp | 67 + .../include/asio/ssl/detail/shutdown_op.hpp | 54 + .../include/asio/ssl/detail/stream_core.hpp | 134 + .../asio/ssl/detail/verify_callback.hpp | 62 + .../asio/include/asio/ssl/detail/write_op.hpp | 67 + subprojects/asio/include/asio/ssl/error.hpp | 111 + .../asio/include/asio/ssl/impl/context.hpp | 67 + .../asio/include/asio/ssl/impl/context.ipp | 1204 ++++++ .../asio/include/asio/ssl/impl/error.ipp | 100 + .../asio/ssl/impl/rfc2818_verification.ipp | 160 + .../asio/include/asio/ssl/impl/src.hpp | 28 + .../include/asio/ssl/rfc2818_verification.hpp | 94 + subprojects/asio/include/asio/ssl/stream.hpp | 762 ++++ .../asio/include/asio/ssl/stream_base.hpp | 52 + .../asio/include/asio/ssl/verify_context.hpp | 67 + .../asio/include/asio/ssl/verify_mode.hpp | 63 + .../asio/include/asio/steady_timer.hpp | 42 + subprojects/asio/include/asio/strand.hpp | 286 ++ .../include/asio/stream_socket_service.hpp | 412 ++ subprojects/asio/include/asio/streambuf.hpp | 33 + .../asio/include/asio/system_context.hpp | 78 + .../asio/include/asio/system_error.hpp | 131 + .../asio/include/asio/system_executor.hpp | 129 + .../asio/include/asio/system_timer.hpp | 42 + subprojects/asio/include/asio/thread.hpp | 92 + subprojects/asio/include/asio/thread_pool.hpp | 232 ++ subprojects/asio/include/asio/time_traits.hpp | 86 + subprojects/asio/include/asio/ts/buffer.hpp | 24 + subprojects/asio/include/asio/ts/executor.hpp | 35 + subprojects/asio/include/asio/ts/internet.hpp | 40 + .../asio/include/asio/ts/io_context.hpp | 20 + subprojects/asio/include/asio/ts/net.hpp | 26 + subprojects/asio/include/asio/ts/netfwd.hpp | 197 + subprojects/asio/include/asio/ts/socket.hpp | 27 + subprojects/asio/include/asio/ts/timer.hpp | 26 + subprojects/asio/include/asio/unyield.hpp | 21 + subprojects/asio/include/asio/use_future.hpp | 160 + .../asio/include/asio/uses_executor.hpp | 71 + subprojects/asio/include/asio/version.hpp | 23 + subprojects/asio/include/asio/wait_traits.hpp | 56 + .../include/asio/waitable_timer_service.hpp | 210 + .../include/asio/windows/basic_handle.hpp | 273 ++ .../asio/windows/basic_object_handle.hpp | 182 + .../windows/basic_random_access_handle.hpp | 376 ++ .../asio/windows/basic_stream_handle.hpp | 359 ++ .../include/asio/windows/object_handle.hpp | 381 ++ .../asio/windows/object_handle_service.hpp | 183 + .../asio/windows/overlapped_handle.hpp | 331 ++ .../include/asio/windows/overlapped_ptr.hpp | 116 + .../asio/windows/random_access_handle.hpp | 378 ++ .../windows/random_access_handle_service.hpp | 214 + .../include/asio/windows/stream_handle.hpp | 362 ++ .../asio/windows/stream_handle_service.hpp | 210 + subprojects/asio/include/asio/write.hpp | 928 +++++ subprojects/asio/include/asio/write_at.hpp | 679 ++++ subprojects/asio/include/asio/yield.hpp | 23 + subprojects/valijson/Authors | 38 + subprojects/valijson/LICENSE | 23 + .../valijson/include/compat/optional.hpp | 1043 +++++ .../include/valijson/adapters/adapter.hpp | 472 +++ .../valijson/adapters/basic_adapter.hpp | 859 ++++ .../valijson/adapters/frozen_value.hpp | 52 + .../valijson/adapters/json11_adapter.hpp | 711 ++++ .../valijson/adapters/jsoncpp_adapter.hpp | 724 ++++ .../adapters/nlohmann_json_adapter.hpp | 713 ++++ .../valijson/adapters/picojson_adapter.hpp | 732 ++++ .../valijson/adapters/poco_json_adapter.hpp | 720 ++++ .../adapters/property_tree_adapter.hpp | 753 ++++ .../valijson/adapters/qtjson_adapter.hpp | 722 ++++ .../valijson/adapters/rapidjson_adapter.hpp | 937 +++++ .../valijson/adapters/std_string_adapter.hpp | 491 +++ .../include/valijson/constraint_builder.hpp | 21 + .../valijson/constraints/basic_constraint.hpp | 55 + .../constraints/concrete_constraints.hpp | 1247 ++++++ .../valijson/constraints/constraint.hpp | 50 + .../constraints/constraint_visitor.hpp | 104 + .../valijson/include/valijson/exceptions.hpp | 41 + .../valijson/internal/custom_allocator.hpp | 107 + .../include/valijson/internal/debug.hpp | 30 + .../valijson/internal/json_pointer.hpp | 275 ++ .../valijson/internal/json_reference.hpp | 60 + .../include/valijson/internal/optional.hpp | 14 + .../include/valijson/internal/uri.hpp | 33 + .../valijson/include/valijson/schema.hpp | 224 ++ .../include/valijson/schema_parser.hpp | 2282 +++++++++++ .../valijson/include/valijson/subschema.hpp | 311 ++ .../include/valijson/utils/file_utils.hpp | 44 + .../include/valijson/utils/json11_utils.hpp | 34 + .../include/valijson/utils/jsoncpp_utils.hpp | 35 + .../valijson/utils/nlohmann_json_utils.hpp | 43 + .../include/valijson/utils/picojson_utils.hpp | 39 + .../valijson/utils/poco_json_utils.hpp | 37 + .../valijson/utils/property_tree_utils.hpp | 74 + .../include/valijson/utils/qtjson_utils.hpp | 44 + .../valijson/utils/rapidjson_utils.hpp | 46 + .../include/valijson/utils/utf8_utils.hpp | 63 + .../include/valijson/validation_results.hpp | 107 + .../include/valijson/validation_visitor.hpp | 1792 +++++++++ .../valijson/include/valijson/validator.hpp | 77 + .../websocketpp/websocketpp/CMakeLists.txt | 2 + subprojects/websocketpp/websocketpp/COPYING | 145 + .../websocketpp/websocketpp/base64/base64.hpp | 178 + .../websocketpp/websocketpp/client.hpp | 33 + subprojects/websocketpp/websocketpp/close.hpp | 353 ++ .../websocketpp/websocketpp/common/asio.hpp | 141 + .../websocketpp/common/asio_ssl.hpp | 39 + .../websocketpp/websocketpp/common/chrono.hpp | 68 + .../websocketpp/common/connection_hdl.hpp | 52 + .../websocketpp/websocketpp/common/cpp11.hpp | 162 + .../websocketpp/common/functional.hpp | 105 + .../websocketpp/websocketpp/common/md5.hpp | 448 +++ .../websocketpp/websocketpp/common/memory.hpp | 88 + .../websocketpp/common/network.hpp | 106 + .../websocketpp/common/platforms.hpp | 46 + .../websocketpp/websocketpp/common/random.hpp | 82 + .../websocketpp/websocketpp/common/regex.hpp | 59 + .../websocketpp/websocketpp/common/stdint.hpp | 73 + .../websocketpp/common/system_error.hpp | 84 + .../websocketpp/websocketpp/common/thread.hpp | 88 + .../websocketpp/websocketpp/common/time.hpp | 56 + .../websocketpp/common/type_traits.hpp | 65 + .../websocketpp/concurrency/basic.hpp | 46 + .../websocketpp/concurrency/none.hpp | 80 + .../websocketpp/websocketpp/config/asio.hpp | 77 + .../websocketpp/config/asio_client.hpp | 77 + .../websocketpp/config/asio_no_tls.hpp | 73 + .../websocketpp/config/asio_no_tls_client.hpp | 73 + .../websocketpp/config/boost_config.hpp | 72 + .../websocketpp/websocketpp/config/core.hpp | 297 ++ .../websocketpp/config/core_client.hpp | 294 ++ .../websocketpp/websocketpp/config/debug.hpp | 286 ++ .../websocketpp/config/debug_asio.hpp | 77 + .../websocketpp/config/debug_asio_no_tls.hpp | 73 + .../websocketpp/config/minimal_client.hpp | 72 + .../websocketpp/config/minimal_server.hpp | 312 ++ .../websocketpp/websocketpp/connection.hpp | 1642 ++++++++ .../websocketpp/connection_base.hpp | 38 + .../websocketpp/websocketpp/endpoint.hpp | 700 ++++ .../websocketpp/websocketpp/endpoint_base.hpp | 38 + subprojects/websocketpp/websocketpp/error.hpp | 277 ++ .../websocketpp/extensions/extension.hpp | 102 + .../permessage_deflate/disabled.hpp | 129 + .../extensions/permessage_deflate/enabled.hpp | 817 ++++ subprojects/websocketpp/websocketpp/frame.hpp | 864 ++++ .../websocketpp/http/constants.hpp | 308 ++ .../websocketpp/http/impl/parser.hpp | 200 + .../websocketpp/http/impl/request.hpp | 191 + .../websocketpp/http/impl/response.hpp | 266 ++ .../websocketpp/websocketpp/http/parser.hpp | 629 +++ .../websocketpp/websocketpp/http/request.hpp | 124 + .../websocketpp/websocketpp/http/response.hpp | 188 + .../websocketpp/impl/connection_impl.hpp | 2375 +++++++++++ .../websocketpp/impl/endpoint_impl.hpp | 269 ++ .../websocketpp/impl/utilities_impl.hpp | 87 + .../websocketpp/websocketpp/logger/basic.hpp | 199 + .../websocketpp/websocketpp/logger/levels.hpp | 203 + .../websocketpp/websocketpp/logger/stub.hpp | 119 + .../websocketpp/websocketpp/logger/syslog.hpp | 146 + .../websocketpp/message_buffer/alloc.hpp | 105 + .../websocketpp/message_buffer/message.hpp | 340 ++ .../websocketpp/message_buffer/pool.hpp | 229 ++ .../websocketpp/processors/base.hpp | 299 ++ .../websocketpp/processors/hybi00.hpp | 462 +++ .../websocketpp/processors/hybi07.hpp | 78 + .../websocketpp/processors/hybi08.hpp | 83 + .../websocketpp/processors/hybi13.hpp | 1078 +++++ .../websocketpp/processors/processor.hpp | 407 ++ .../websocketpp/websocketpp/random/none.hpp | 60 + .../websocketpp/random/random_device.hpp | 80 + .../websocketpp/roles/client_endpoint.hpp | 173 + .../websocketpp/roles/server_endpoint.hpp | 195 + .../websocketpp/websocketpp/server.hpp | 33 + .../websocketpp/websocketpp/sha1/sha1.hpp | 189 + .../websocketpp/transport/asio/base.hpp | 232 ++ .../websocketpp/transport/asio/connection.hpp | 1197 ++++++ .../websocketpp/transport/asio/endpoint.hpp | 1186 ++++++ .../transport/asio/security/base.hpp | 159 + .../transport/asio/security/none.hpp | 373 ++ .../transport/asio/security/tls.hpp | 475 +++ .../websocketpp/transport/base/connection.hpp | 238 ++ .../websocketpp/transport/base/endpoint.hpp | 77 + .../websocketpp/transport/iostream/base.hpp | 133 + .../transport/iostream/connection.hpp | 714 ++++ .../transport/iostream/endpoint.hpp | 222 + .../websocketpp/transport/stub/base.hpp | 95 + .../websocketpp/transport/stub/connection.hpp | 286 ++ .../websocketpp/transport/stub/endpoint.hpp | 140 + subprojects/websocketpp/websocketpp/uri.hpp | 356 ++ .../websocketpp/utf8_validator.hpp | 154 + .../websocketpp/websocketpp/utilities.hpp | 180 + .../websocketpp/websocketpp/version.hpp | 61 + subprojects/wswrap | 1 + 627 files changed, 150875 insertions(+) create mode 100644 soh/soh/Network/Archipelago/Archipelago.cpp create mode 100644 soh/soh/Network/Archipelago/Archipelago.h create mode 160000 subprojects/apclientpp create mode 100644 subprojects/asio/COPYING create mode 100644 subprojects/asio/LICENSE_1_0.txt create mode 100644 subprojects/asio/include/asio.hpp create mode 100644 subprojects/asio/include/asio/COPYING create mode 100644 subprojects/asio/include/asio/LICENSE_1_0.txt create mode 100644 subprojects/asio/include/asio/associated_allocator.hpp create mode 100644 subprojects/asio/include/asio/associated_executor.hpp create mode 100644 subprojects/asio/include/asio/async_result.hpp create mode 100644 subprojects/asio/include/asio/basic_datagram_socket.hpp create mode 100644 subprojects/asio/include/asio/basic_deadline_timer.hpp create mode 100644 subprojects/asio/include/asio/basic_io_object.hpp create mode 100644 subprojects/asio/include/asio/basic_raw_socket.hpp create mode 100644 subprojects/asio/include/asio/basic_seq_packet_socket.hpp create mode 100644 subprojects/asio/include/asio/basic_serial_port.hpp create mode 100644 subprojects/asio/include/asio/basic_signal_set.hpp create mode 100644 subprojects/asio/include/asio/basic_socket.hpp create mode 100644 subprojects/asio/include/asio/basic_socket_acceptor.hpp create mode 100644 subprojects/asio/include/asio/basic_socket_iostream.hpp create mode 100644 subprojects/asio/include/asio/basic_socket_streambuf.hpp create mode 100644 subprojects/asio/include/asio/basic_stream_socket.hpp create mode 100644 subprojects/asio/include/asio/basic_streambuf.hpp create mode 100644 subprojects/asio/include/asio/basic_streambuf_fwd.hpp create mode 100644 subprojects/asio/include/asio/basic_waitable_timer.hpp create mode 100644 subprojects/asio/include/asio/bind_executor.hpp create mode 100644 subprojects/asio/include/asio/buffer.hpp create mode 100644 subprojects/asio/include/asio/buffered_read_stream.hpp create mode 100644 subprojects/asio/include/asio/buffered_read_stream_fwd.hpp create mode 100644 subprojects/asio/include/asio/buffered_stream.hpp create mode 100644 subprojects/asio/include/asio/buffered_stream_fwd.hpp create mode 100644 subprojects/asio/include/asio/buffered_write_stream.hpp create mode 100644 subprojects/asio/include/asio/buffered_write_stream_fwd.hpp create mode 100644 subprojects/asio/include/asio/buffers_iterator.hpp create mode 100644 subprojects/asio/include/asio/completion_condition.hpp create mode 100644 subprojects/asio/include/asio/connect.hpp create mode 100644 subprojects/asio/include/asio/coroutine.hpp create mode 100644 subprojects/asio/include/asio/datagram_socket_service.hpp create mode 100644 subprojects/asio/include/asio/deadline_timer.hpp create mode 100644 subprojects/asio/include/asio/deadline_timer_service.hpp create mode 100644 subprojects/asio/include/asio/defer.hpp create mode 100644 subprojects/asio/include/asio/detail/array.hpp create mode 100644 subprojects/asio/include/asio/detail/array_fwd.hpp create mode 100644 subprojects/asio/include/asio/detail/assert.hpp create mode 100644 subprojects/asio/include/asio/detail/atomic_count.hpp create mode 100644 subprojects/asio/include/asio/detail/base_from_completion_cond.hpp create mode 100644 subprojects/asio/include/asio/detail/bind_handler.hpp create mode 100644 subprojects/asio/include/asio/detail/buffer_resize_guard.hpp create mode 100644 subprojects/asio/include/asio/detail/buffer_sequence_adapter.hpp create mode 100644 subprojects/asio/include/asio/detail/buffered_stream_storage.hpp create mode 100644 subprojects/asio/include/asio/detail/call_stack.hpp create mode 100644 subprojects/asio/include/asio/detail/chrono.hpp create mode 100644 subprojects/asio/include/asio/detail/chrono_time_traits.hpp create mode 100644 subprojects/asio/include/asio/detail/completion_handler.hpp create mode 100644 subprojects/asio/include/asio/detail/concurrency_hint.hpp create mode 100644 subprojects/asio/include/asio/detail/conditionally_enabled_event.hpp create mode 100644 subprojects/asio/include/asio/detail/conditionally_enabled_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/config.hpp create mode 100644 subprojects/asio/include/asio/detail/consuming_buffers.hpp create mode 100644 subprojects/asio/include/asio/detail/cstddef.hpp create mode 100644 subprojects/asio/include/asio/detail/cstdint.hpp create mode 100644 subprojects/asio/include/asio/detail/date_time_fwd.hpp create mode 100644 subprojects/asio/include/asio/detail/deadline_timer_service.hpp create mode 100644 subprojects/asio/include/asio/detail/dependent_type.hpp create mode 100644 subprojects/asio/include/asio/detail/descriptor_ops.hpp create mode 100644 subprojects/asio/include/asio/detail/descriptor_read_op.hpp create mode 100644 subprojects/asio/include/asio/detail/descriptor_write_op.hpp create mode 100644 subprojects/asio/include/asio/detail/dev_poll_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/epoll_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/event.hpp create mode 100644 subprojects/asio/include/asio/detail/eventfd_select_interrupter.hpp create mode 100644 subprojects/asio/include/asio/detail/executor_op.hpp create mode 100644 subprojects/asio/include/asio/detail/fd_set_adapter.hpp create mode 100644 subprojects/asio/include/asio/detail/fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/functional.hpp create mode 100644 subprojects/asio/include/asio/detail/future.hpp create mode 100644 subprojects/asio/include/asio/detail/gcc_arm_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/gcc_hppa_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/gcc_sync_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/gcc_x86_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/global.hpp create mode 100644 subprojects/asio/include/asio/detail/handler_alloc_helpers.hpp create mode 100644 subprojects/asio/include/asio/detail/handler_cont_helpers.hpp create mode 100644 subprojects/asio/include/asio/detail/handler_invoke_helpers.hpp create mode 100644 subprojects/asio/include/asio/detail/handler_tracking.hpp create mode 100644 subprojects/asio/include/asio/detail/handler_type_requirements.hpp create mode 100644 subprojects/asio/include/asio/detail/handler_work.hpp create mode 100644 subprojects/asio/include/asio/detail/hash_map.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/buffer_sequence_adapter.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/descriptor_ops.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/dev_poll_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/dev_poll_reactor.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/epoll_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/epoll_reactor.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/eventfd_select_interrupter.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/handler_tracking.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/kqueue_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/kqueue_reactor.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/null_event.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/pipe_select_interrupter.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/posix_event.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/posix_mutex.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/posix_thread.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/posix_tss_ptr.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/reactive_descriptor_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/reactive_serial_port_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/reactive_socket_service_base.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/resolver_service_base.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/scheduler.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/select_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/select_reactor.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/service_registry.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/service_registry.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/signal_set_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/socket_ops.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/socket_select_interrupter.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/strand_executor_service.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/strand_executor_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/strand_service.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/strand_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/throw_error.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/timer_queue_ptime.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/timer_queue_set.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_event.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_iocp_handle_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_iocp_io_context.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/win_iocp_io_context.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_iocp_serial_port_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_iocp_socket_service_base.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_mutex.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_object_handle_service.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_static_mutex.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_thread.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/win_tss_ptr.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/winrt_ssocket_service_base.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.hpp create mode 100644 subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.ipp create mode 100644 subprojects/asio/include/asio/detail/impl/winsock_init.ipp create mode 100644 subprojects/asio/include/asio/detail/io_control.hpp create mode 100644 subprojects/asio/include/asio/detail/is_buffer_sequence.hpp create mode 100644 subprojects/asio/include/asio/detail/is_executor.hpp create mode 100644 subprojects/asio/include/asio/detail/keyword_tss_ptr.hpp create mode 100644 subprojects/asio/include/asio/detail/kqueue_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/limits.hpp create mode 100644 subprojects/asio/include/asio/detail/local_free_on_block_exit.hpp create mode 100644 subprojects/asio/include/asio/detail/macos_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/memory.hpp create mode 100644 subprojects/asio/include/asio/detail/mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/noncopyable.hpp create mode 100644 subprojects/asio/include/asio/detail/null_event.hpp create mode 100644 subprojects/asio/include/asio/detail/null_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/null_global.hpp create mode 100644 subprojects/asio/include/asio/detail/null_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/null_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/null_signal_blocker.hpp create mode 100644 subprojects/asio/include/asio/detail/null_socket_service.hpp create mode 100644 subprojects/asio/include/asio/detail/null_static_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/null_thread.hpp create mode 100644 subprojects/asio/include/asio/detail/null_tss_ptr.hpp create mode 100644 subprojects/asio/include/asio/detail/object_pool.hpp create mode 100644 subprojects/asio/include/asio/detail/old_win_sdk_compat.hpp create mode 100644 subprojects/asio/include/asio/detail/op_queue.hpp create mode 100644 subprojects/asio/include/asio/detail/operation.hpp create mode 100644 subprojects/asio/include/asio/detail/pipe_select_interrupter.hpp create mode 100644 subprojects/asio/include/asio/detail/pop_options.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_event.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_fd_set_adapter.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_global.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_signal_blocker.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_static_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_thread.hpp create mode 100644 subprojects/asio/include/asio/detail/posix_tss_ptr.hpp create mode 100644 subprojects/asio/include/asio/detail/push_options.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_descriptor_service.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_null_buffers_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_serial_port_service.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_accept_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_connect_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_recv_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_recvfrom_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_recvmsg_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_send_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_sendto_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_service.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_socket_service_base.hpp create mode 100644 subprojects/asio/include/asio/detail/reactive_wait_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/reactor_fwd.hpp create mode 100644 subprojects/asio/include/asio/detail/reactor_op.hpp create mode 100644 subprojects/asio/include/asio/detail/reactor_op_queue.hpp create mode 100644 subprojects/asio/include/asio/detail/recycling_allocator.hpp create mode 100644 subprojects/asio/include/asio/detail/regex_fwd.hpp create mode 100644 subprojects/asio/include/asio/detail/resolve_endpoint_op.hpp create mode 100644 subprojects/asio/include/asio/detail/resolve_op.hpp create mode 100644 subprojects/asio/include/asio/detail/resolve_query_op.hpp create mode 100644 subprojects/asio/include/asio/detail/resolver_service.hpp create mode 100644 subprojects/asio/include/asio/detail/resolver_service_base.hpp create mode 100644 subprojects/asio/include/asio/detail/scheduler.hpp create mode 100644 subprojects/asio/include/asio/detail/scheduler_operation.hpp create mode 100644 subprojects/asio/include/asio/detail/scheduler_thread_info.hpp create mode 100644 subprojects/asio/include/asio/detail/scoped_lock.hpp create mode 100644 subprojects/asio/include/asio/detail/scoped_ptr.hpp create mode 100644 subprojects/asio/include/asio/detail/select_interrupter.hpp create mode 100644 subprojects/asio/include/asio/detail/select_reactor.hpp create mode 100644 subprojects/asio/include/asio/detail/service_registry.hpp create mode 100644 subprojects/asio/include/asio/detail/signal_blocker.hpp create mode 100644 subprojects/asio/include/asio/detail/signal_handler.hpp create mode 100644 subprojects/asio/include/asio/detail/signal_init.hpp create mode 100644 subprojects/asio/include/asio/detail/signal_op.hpp create mode 100644 subprojects/asio/include/asio/detail/signal_set_service.hpp create mode 100644 subprojects/asio/include/asio/detail/socket_holder.hpp create mode 100644 subprojects/asio/include/asio/detail/socket_ops.hpp create mode 100644 subprojects/asio/include/asio/detail/socket_option.hpp create mode 100644 subprojects/asio/include/asio/detail/socket_select_interrupter.hpp create mode 100644 subprojects/asio/include/asio/detail/socket_types.hpp create mode 100644 subprojects/asio/include/asio/detail/solaris_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/static_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/std_event.hpp create mode 100644 subprojects/asio/include/asio/detail/std_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/std_global.hpp create mode 100644 subprojects/asio/include/asio/detail/std_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/std_static_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/std_thread.hpp create mode 100644 subprojects/asio/include/asio/detail/strand_executor_service.hpp create mode 100644 subprojects/asio/include/asio/detail/strand_service.hpp create mode 100644 subprojects/asio/include/asio/detail/string_view.hpp create mode 100644 subprojects/asio/include/asio/detail/thread.hpp create mode 100644 subprojects/asio/include/asio/detail/thread_context.hpp create mode 100644 subprojects/asio/include/asio/detail/thread_group.hpp create mode 100644 subprojects/asio/include/asio/detail/thread_info_base.hpp create mode 100644 subprojects/asio/include/asio/detail/throw_error.hpp create mode 100644 subprojects/asio/include/asio/detail/throw_exception.hpp create mode 100644 subprojects/asio/include/asio/detail/timer_queue.hpp create mode 100644 subprojects/asio/include/asio/detail/timer_queue_base.hpp create mode 100644 subprojects/asio/include/asio/detail/timer_queue_ptime.hpp create mode 100644 subprojects/asio/include/asio/detail/timer_queue_set.hpp create mode 100644 subprojects/asio/include/asio/detail/timer_scheduler.hpp create mode 100644 subprojects/asio/include/asio/detail/timer_scheduler_fwd.hpp create mode 100644 subprojects/asio/include/asio/detail/tss_ptr.hpp create mode 100644 subprojects/asio/include/asio/detail/type_traits.hpp create mode 100644 subprojects/asio/include/asio/detail/variadic_templates.hpp create mode 100644 subprojects/asio/include/asio/detail/wait_handler.hpp create mode 100644 subprojects/asio/include/asio/detail/wait_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_event.hpp create mode 100644 subprojects/asio/include/asio/detail/win_fd_set_adapter.hpp create mode 100644 subprojects/asio/include/asio/detail/win_fenced_block.hpp create mode 100644 subprojects/asio/include/asio/detail/win_global.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_handle_read_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_handle_service.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_handle_write_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_io_context.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_null_buffers_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_operation.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_overlapped_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_overlapped_ptr.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_serial_port_service.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_accept_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_connect_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_recv_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_recvfrom_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_recvmsg_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_send_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_service.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_socket_service_base.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_thread_info.hpp create mode 100644 subprojects/asio/include/asio/detail/win_iocp_wait_op.hpp create mode 100644 subprojects/asio/include/asio/detail/win_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/win_object_handle_service.hpp create mode 100644 subprojects/asio/include/asio/detail/win_static_mutex.hpp create mode 100644 subprojects/asio/include/asio/detail/win_thread.hpp create mode 100644 subprojects/asio/include/asio/detail/win_tss_ptr.hpp create mode 100644 subprojects/asio/include/asio/detail/winapp_thread.hpp create mode 100644 subprojects/asio/include/asio/detail/wince_thread.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_async_manager.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_async_op.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_resolve_op.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_resolver_service.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_socket_connect_op.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_socket_recv_op.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_socket_send_op.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_ssocket_service.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_ssocket_service_base.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_timer_scheduler.hpp create mode 100644 subprojects/asio/include/asio/detail/winrt_utils.hpp create mode 100644 subprojects/asio/include/asio/detail/winsock_init.hpp create mode 100644 subprojects/asio/include/asio/detail/work_dispatcher.hpp create mode 100644 subprojects/asio/include/asio/detail/wrapped_handler.hpp create mode 100644 subprojects/asio/include/asio/dispatch.hpp create mode 100644 subprojects/asio/include/asio/error.hpp create mode 100644 subprojects/asio/include/asio/error_code.hpp create mode 100644 subprojects/asio/include/asio/execution_context.hpp create mode 100644 subprojects/asio/include/asio/executor.hpp create mode 100644 subprojects/asio/include/asio/executor_work_guard.hpp create mode 100644 subprojects/asio/include/asio/experimental.hpp create mode 100644 subprojects/asio/include/asio/experimental/co_spawn.hpp create mode 100644 subprojects/asio/include/asio/experimental/detached.hpp create mode 100644 subprojects/asio/include/asio/experimental/impl/co_spawn.hpp create mode 100644 subprojects/asio/include/asio/experimental/impl/detached.hpp create mode 100644 subprojects/asio/include/asio/experimental/impl/redirect_error.hpp create mode 100644 subprojects/asio/include/asio/experimental/redirect_error.hpp create mode 100644 subprojects/asio/include/asio/generic/basic_endpoint.hpp create mode 100644 subprojects/asio/include/asio/generic/datagram_protocol.hpp create mode 100644 subprojects/asio/include/asio/generic/detail/endpoint.hpp create mode 100644 subprojects/asio/include/asio/generic/detail/impl/endpoint.ipp create mode 100644 subprojects/asio/include/asio/generic/raw_protocol.hpp create mode 100644 subprojects/asio/include/asio/generic/seq_packet_protocol.hpp create mode 100644 subprojects/asio/include/asio/generic/stream_protocol.hpp create mode 100644 subprojects/asio/include/asio/handler_alloc_hook.hpp create mode 100644 subprojects/asio/include/asio/handler_continuation_hook.hpp create mode 100644 subprojects/asio/include/asio/handler_invoke_hook.hpp create mode 100644 subprojects/asio/include/asio/handler_type.hpp create mode 100644 subprojects/asio/include/asio/high_resolution_timer.hpp create mode 100644 subprojects/asio/include/asio/impl/buffered_read_stream.hpp create mode 100644 subprojects/asio/include/asio/impl/buffered_write_stream.hpp create mode 100644 subprojects/asio/include/asio/impl/connect.hpp create mode 100644 subprojects/asio/include/asio/impl/defer.hpp create mode 100644 subprojects/asio/include/asio/impl/dispatch.hpp create mode 100644 subprojects/asio/include/asio/impl/error.ipp create mode 100644 subprojects/asio/include/asio/impl/error_code.ipp create mode 100644 subprojects/asio/include/asio/impl/execution_context.hpp create mode 100644 subprojects/asio/include/asio/impl/execution_context.ipp create mode 100644 subprojects/asio/include/asio/impl/executor.hpp create mode 100644 subprojects/asio/include/asio/impl/executor.ipp create mode 100644 subprojects/asio/include/asio/impl/handler_alloc_hook.ipp create mode 100644 subprojects/asio/include/asio/impl/io_context.hpp create mode 100644 subprojects/asio/include/asio/impl/io_context.ipp create mode 100644 subprojects/asio/include/asio/impl/post.hpp create mode 100644 subprojects/asio/include/asio/impl/read.hpp create mode 100644 subprojects/asio/include/asio/impl/read_at.hpp create mode 100644 subprojects/asio/include/asio/impl/read_until.hpp create mode 100644 subprojects/asio/include/asio/impl/serial_port_base.hpp create mode 100644 subprojects/asio/include/asio/impl/serial_port_base.ipp create mode 100644 subprojects/asio/include/asio/impl/spawn.hpp create mode 100644 subprojects/asio/include/asio/impl/src.cpp create mode 100644 subprojects/asio/include/asio/impl/src.hpp create mode 100644 subprojects/asio/include/asio/impl/system_context.hpp create mode 100644 subprojects/asio/include/asio/impl/system_context.ipp create mode 100644 subprojects/asio/include/asio/impl/system_executor.hpp create mode 100644 subprojects/asio/include/asio/impl/thread_pool.hpp create mode 100644 subprojects/asio/include/asio/impl/thread_pool.ipp create mode 100644 subprojects/asio/include/asio/impl/use_future.hpp create mode 100644 subprojects/asio/include/asio/impl/write.hpp create mode 100644 subprojects/asio/include/asio/impl/write_at.hpp create mode 100644 subprojects/asio/include/asio/io_context.hpp create mode 100644 subprojects/asio/include/asio/io_context_strand.hpp create mode 100644 subprojects/asio/include/asio/io_service.hpp create mode 100644 subprojects/asio/include/asio/io_service_strand.hpp create mode 100644 subprojects/asio/include/asio/ip/address.hpp create mode 100644 subprojects/asio/include/asio/ip/address_v4.hpp create mode 100644 subprojects/asio/include/asio/ip/address_v4_iterator.hpp create mode 100644 subprojects/asio/include/asio/ip/address_v4_range.hpp create mode 100644 subprojects/asio/include/asio/ip/address_v6.hpp create mode 100644 subprojects/asio/include/asio/ip/address_v6_iterator.hpp create mode 100644 subprojects/asio/include/asio/ip/address_v6_range.hpp create mode 100644 subprojects/asio/include/asio/ip/bad_address_cast.hpp create mode 100644 subprojects/asio/include/asio/ip/basic_endpoint.hpp create mode 100644 subprojects/asio/include/asio/ip/basic_resolver.hpp create mode 100644 subprojects/asio/include/asio/ip/basic_resolver_entry.hpp create mode 100644 subprojects/asio/include/asio/ip/basic_resolver_iterator.hpp create mode 100644 subprojects/asio/include/asio/ip/basic_resolver_query.hpp create mode 100644 subprojects/asio/include/asio/ip/basic_resolver_results.hpp create mode 100644 subprojects/asio/include/asio/ip/detail/endpoint.hpp create mode 100644 subprojects/asio/include/asio/ip/detail/impl/endpoint.ipp create mode 100644 subprojects/asio/include/asio/ip/detail/socket_option.hpp create mode 100644 subprojects/asio/include/asio/ip/host_name.hpp create mode 100644 subprojects/asio/include/asio/ip/icmp.hpp create mode 100644 subprojects/asio/include/asio/ip/impl/address.hpp create mode 100644 subprojects/asio/include/asio/ip/impl/address.ipp create mode 100644 subprojects/asio/include/asio/ip/impl/address_v4.hpp create mode 100644 subprojects/asio/include/asio/ip/impl/address_v4.ipp create mode 100644 subprojects/asio/include/asio/ip/impl/address_v6.hpp create mode 100644 subprojects/asio/include/asio/ip/impl/address_v6.ipp create mode 100644 subprojects/asio/include/asio/ip/impl/basic_endpoint.hpp create mode 100644 subprojects/asio/include/asio/ip/impl/host_name.ipp create mode 100644 subprojects/asio/include/asio/ip/impl/network_v4.hpp create mode 100644 subprojects/asio/include/asio/ip/impl/network_v4.ipp create mode 100644 subprojects/asio/include/asio/ip/impl/network_v6.hpp create mode 100644 subprojects/asio/include/asio/ip/impl/network_v6.ipp create mode 100644 subprojects/asio/include/asio/ip/multicast.hpp create mode 100644 subprojects/asio/include/asio/ip/network_v4.hpp create mode 100644 subprojects/asio/include/asio/ip/network_v6.hpp create mode 100644 subprojects/asio/include/asio/ip/resolver_base.hpp create mode 100644 subprojects/asio/include/asio/ip/resolver_query_base.hpp create mode 100644 subprojects/asio/include/asio/ip/resolver_service.hpp create mode 100644 subprojects/asio/include/asio/ip/tcp.hpp create mode 100644 subprojects/asio/include/asio/ip/udp.hpp create mode 100644 subprojects/asio/include/asio/ip/unicast.hpp create mode 100644 subprojects/asio/include/asio/ip/v6_only.hpp create mode 100644 subprojects/asio/include/asio/is_executor.hpp create mode 100644 subprojects/asio/include/asio/is_read_buffered.hpp create mode 100644 subprojects/asio/include/asio/is_write_buffered.hpp create mode 100644 subprojects/asio/include/asio/local/basic_endpoint.hpp create mode 100644 subprojects/asio/include/asio/local/connect_pair.hpp create mode 100644 subprojects/asio/include/asio/local/datagram_protocol.hpp create mode 100644 subprojects/asio/include/asio/local/detail/endpoint.hpp create mode 100644 subprojects/asio/include/asio/local/detail/impl/endpoint.ipp create mode 100644 subprojects/asio/include/asio/local/stream_protocol.hpp create mode 100644 subprojects/asio/include/asio/packaged_task.hpp create mode 100644 subprojects/asio/include/asio/placeholders.hpp create mode 100644 subprojects/asio/include/asio/posix/basic_descriptor.hpp create mode 100644 subprojects/asio/include/asio/posix/basic_stream_descriptor.hpp create mode 100644 subprojects/asio/include/asio/posix/descriptor.hpp create mode 100644 subprojects/asio/include/asio/posix/descriptor_base.hpp create mode 100644 subprojects/asio/include/asio/posix/stream_descriptor.hpp create mode 100644 subprojects/asio/include/asio/posix/stream_descriptor_service.hpp create mode 100644 subprojects/asio/include/asio/post.hpp create mode 100644 subprojects/asio/include/asio/raw_socket_service.hpp create mode 100644 subprojects/asio/include/asio/read.hpp create mode 100644 subprojects/asio/include/asio/read_at.hpp create mode 100644 subprojects/asio/include/asio/read_until.hpp create mode 100644 subprojects/asio/include/asio/seq_packet_socket_service.hpp create mode 100644 subprojects/asio/include/asio/serial_port.hpp create mode 100644 subprojects/asio/include/asio/serial_port_base.hpp create mode 100644 subprojects/asio/include/asio/serial_port_service.hpp create mode 100644 subprojects/asio/include/asio/signal_set.hpp create mode 100644 subprojects/asio/include/asio/signal_set_service.hpp create mode 100644 subprojects/asio/include/asio/socket_acceptor_service.hpp create mode 100644 subprojects/asio/include/asio/socket_base.hpp create mode 100644 subprojects/asio/include/asio/spawn.hpp create mode 100644 subprojects/asio/include/asio/ssl.hpp create mode 100644 subprojects/asio/include/asio/ssl/context.hpp create mode 100644 subprojects/asio/include/asio/ssl/context_base.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/buffered_handshake_op.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/engine.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/handshake_op.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/impl/engine.ipp create mode 100644 subprojects/asio/include/asio/ssl/detail/impl/openssl_init.ipp create mode 100644 subprojects/asio/include/asio/ssl/detail/io.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/openssl_init.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/openssl_types.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/password_callback.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/read_op.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/shutdown_op.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/stream_core.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/verify_callback.hpp create mode 100644 subprojects/asio/include/asio/ssl/detail/write_op.hpp create mode 100644 subprojects/asio/include/asio/ssl/error.hpp create mode 100644 subprojects/asio/include/asio/ssl/impl/context.hpp create mode 100644 subprojects/asio/include/asio/ssl/impl/context.ipp create mode 100644 subprojects/asio/include/asio/ssl/impl/error.ipp create mode 100644 subprojects/asio/include/asio/ssl/impl/rfc2818_verification.ipp create mode 100644 subprojects/asio/include/asio/ssl/impl/src.hpp create mode 100644 subprojects/asio/include/asio/ssl/rfc2818_verification.hpp create mode 100644 subprojects/asio/include/asio/ssl/stream.hpp create mode 100644 subprojects/asio/include/asio/ssl/stream_base.hpp create mode 100644 subprojects/asio/include/asio/ssl/verify_context.hpp create mode 100644 subprojects/asio/include/asio/ssl/verify_mode.hpp create mode 100644 subprojects/asio/include/asio/steady_timer.hpp create mode 100644 subprojects/asio/include/asio/strand.hpp create mode 100644 subprojects/asio/include/asio/stream_socket_service.hpp create mode 100644 subprojects/asio/include/asio/streambuf.hpp create mode 100644 subprojects/asio/include/asio/system_context.hpp create mode 100644 subprojects/asio/include/asio/system_error.hpp create mode 100644 subprojects/asio/include/asio/system_executor.hpp create mode 100644 subprojects/asio/include/asio/system_timer.hpp create mode 100644 subprojects/asio/include/asio/thread.hpp create mode 100644 subprojects/asio/include/asio/thread_pool.hpp create mode 100644 subprojects/asio/include/asio/time_traits.hpp create mode 100644 subprojects/asio/include/asio/ts/buffer.hpp create mode 100644 subprojects/asio/include/asio/ts/executor.hpp create mode 100644 subprojects/asio/include/asio/ts/internet.hpp create mode 100644 subprojects/asio/include/asio/ts/io_context.hpp create mode 100644 subprojects/asio/include/asio/ts/net.hpp create mode 100644 subprojects/asio/include/asio/ts/netfwd.hpp create mode 100644 subprojects/asio/include/asio/ts/socket.hpp create mode 100644 subprojects/asio/include/asio/ts/timer.hpp create mode 100644 subprojects/asio/include/asio/unyield.hpp create mode 100644 subprojects/asio/include/asio/use_future.hpp create mode 100644 subprojects/asio/include/asio/uses_executor.hpp create mode 100644 subprojects/asio/include/asio/version.hpp create mode 100644 subprojects/asio/include/asio/wait_traits.hpp create mode 100644 subprojects/asio/include/asio/waitable_timer_service.hpp create mode 100644 subprojects/asio/include/asio/windows/basic_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/basic_object_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/basic_random_access_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/basic_stream_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/object_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/object_handle_service.hpp create mode 100644 subprojects/asio/include/asio/windows/overlapped_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/overlapped_ptr.hpp create mode 100644 subprojects/asio/include/asio/windows/random_access_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/random_access_handle_service.hpp create mode 100644 subprojects/asio/include/asio/windows/stream_handle.hpp create mode 100644 subprojects/asio/include/asio/windows/stream_handle_service.hpp create mode 100644 subprojects/asio/include/asio/write.hpp create mode 100644 subprojects/asio/include/asio/write_at.hpp create mode 100644 subprojects/asio/include/asio/yield.hpp create mode 100644 subprojects/valijson/Authors create mode 100644 subprojects/valijson/LICENSE create mode 100644 subprojects/valijson/include/compat/optional.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/basic_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/frozen_value.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/json11_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/jsoncpp_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/nlohmann_json_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/picojson_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/poco_json_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/property_tree_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/qtjson_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/rapidjson_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/adapters/std_string_adapter.hpp create mode 100644 subprojects/valijson/include/valijson/constraint_builder.hpp create mode 100644 subprojects/valijson/include/valijson/constraints/basic_constraint.hpp create mode 100644 subprojects/valijson/include/valijson/constraints/concrete_constraints.hpp create mode 100644 subprojects/valijson/include/valijson/constraints/constraint.hpp create mode 100644 subprojects/valijson/include/valijson/constraints/constraint_visitor.hpp create mode 100644 subprojects/valijson/include/valijson/exceptions.hpp create mode 100644 subprojects/valijson/include/valijson/internal/custom_allocator.hpp create mode 100644 subprojects/valijson/include/valijson/internal/debug.hpp create mode 100644 subprojects/valijson/include/valijson/internal/json_pointer.hpp create mode 100644 subprojects/valijson/include/valijson/internal/json_reference.hpp create mode 100644 subprojects/valijson/include/valijson/internal/optional.hpp create mode 100644 subprojects/valijson/include/valijson/internal/uri.hpp create mode 100644 subprojects/valijson/include/valijson/schema.hpp create mode 100644 subprojects/valijson/include/valijson/schema_parser.hpp create mode 100644 subprojects/valijson/include/valijson/subschema.hpp create mode 100644 subprojects/valijson/include/valijson/utils/file_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/json11_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/jsoncpp_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/nlohmann_json_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/picojson_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/poco_json_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/property_tree_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/qtjson_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/rapidjson_utils.hpp create mode 100644 subprojects/valijson/include/valijson/utils/utf8_utils.hpp create mode 100644 subprojects/valijson/include/valijson/validation_results.hpp create mode 100644 subprojects/valijson/include/valijson/validation_visitor.hpp create mode 100644 subprojects/valijson/include/valijson/validator.hpp create mode 100644 subprojects/websocketpp/websocketpp/CMakeLists.txt create mode 100644 subprojects/websocketpp/websocketpp/COPYING create mode 100644 subprojects/websocketpp/websocketpp/base64/base64.hpp create mode 100644 subprojects/websocketpp/websocketpp/client.hpp create mode 100644 subprojects/websocketpp/websocketpp/close.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/asio.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/asio_ssl.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/chrono.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/connection_hdl.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/cpp11.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/functional.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/md5.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/memory.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/network.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/platforms.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/random.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/regex.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/stdint.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/system_error.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/thread.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/time.hpp create mode 100644 subprojects/websocketpp/websocketpp/common/type_traits.hpp create mode 100644 subprojects/websocketpp/websocketpp/concurrency/basic.hpp create mode 100644 subprojects/websocketpp/websocketpp/concurrency/none.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/asio.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/asio_client.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/asio_no_tls.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/asio_no_tls_client.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/boost_config.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/core.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/core_client.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/debug.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/debug_asio.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/debug_asio_no_tls.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/minimal_client.hpp create mode 100644 subprojects/websocketpp/websocketpp/config/minimal_server.hpp create mode 100644 subprojects/websocketpp/websocketpp/connection.hpp create mode 100644 subprojects/websocketpp/websocketpp/connection_base.hpp create mode 100644 subprojects/websocketpp/websocketpp/endpoint.hpp create mode 100644 subprojects/websocketpp/websocketpp/endpoint_base.hpp create mode 100644 subprojects/websocketpp/websocketpp/error.hpp create mode 100644 subprojects/websocketpp/websocketpp/extensions/extension.hpp create mode 100644 subprojects/websocketpp/websocketpp/extensions/permessage_deflate/disabled.hpp create mode 100644 subprojects/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp create mode 100644 subprojects/websocketpp/websocketpp/frame.hpp create mode 100644 subprojects/websocketpp/websocketpp/http/constants.hpp create mode 100644 subprojects/websocketpp/websocketpp/http/impl/parser.hpp create mode 100644 subprojects/websocketpp/websocketpp/http/impl/request.hpp create mode 100644 subprojects/websocketpp/websocketpp/http/impl/response.hpp create mode 100644 subprojects/websocketpp/websocketpp/http/parser.hpp create mode 100644 subprojects/websocketpp/websocketpp/http/request.hpp create mode 100644 subprojects/websocketpp/websocketpp/http/response.hpp create mode 100644 subprojects/websocketpp/websocketpp/impl/connection_impl.hpp create mode 100644 subprojects/websocketpp/websocketpp/impl/endpoint_impl.hpp create mode 100644 subprojects/websocketpp/websocketpp/impl/utilities_impl.hpp create mode 100644 subprojects/websocketpp/websocketpp/logger/basic.hpp create mode 100644 subprojects/websocketpp/websocketpp/logger/levels.hpp create mode 100644 subprojects/websocketpp/websocketpp/logger/stub.hpp create mode 100644 subprojects/websocketpp/websocketpp/logger/syslog.hpp create mode 100644 subprojects/websocketpp/websocketpp/message_buffer/alloc.hpp create mode 100644 subprojects/websocketpp/websocketpp/message_buffer/message.hpp create mode 100644 subprojects/websocketpp/websocketpp/message_buffer/pool.hpp create mode 100644 subprojects/websocketpp/websocketpp/processors/base.hpp create mode 100644 subprojects/websocketpp/websocketpp/processors/hybi00.hpp create mode 100644 subprojects/websocketpp/websocketpp/processors/hybi07.hpp create mode 100644 subprojects/websocketpp/websocketpp/processors/hybi08.hpp create mode 100644 subprojects/websocketpp/websocketpp/processors/hybi13.hpp create mode 100644 subprojects/websocketpp/websocketpp/processors/processor.hpp create mode 100644 subprojects/websocketpp/websocketpp/random/none.hpp create mode 100644 subprojects/websocketpp/websocketpp/random/random_device.hpp create mode 100644 subprojects/websocketpp/websocketpp/roles/client_endpoint.hpp create mode 100644 subprojects/websocketpp/websocketpp/roles/server_endpoint.hpp create mode 100644 subprojects/websocketpp/websocketpp/server.hpp create mode 100644 subprojects/websocketpp/websocketpp/sha1/sha1.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/asio/base.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/asio/connection.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/asio/endpoint.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/asio/security/base.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/asio/security/none.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/asio/security/tls.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/base/connection.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/base/endpoint.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/iostream/base.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/iostream/connection.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/iostream/endpoint.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/stub/base.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/stub/connection.hpp create mode 100644 subprojects/websocketpp/websocketpp/transport/stub/endpoint.hpp create mode 100644 subprojects/websocketpp/websocketpp/uri.hpp create mode 100644 subprojects/websocketpp/websocketpp/utf8_validator.hpp create mode 100644 subprojects/websocketpp/websocketpp/utilities.hpp create mode 100644 subprojects/websocketpp/websocketpp/version.hpp create mode 160000 subprojects/wswrap diff --git a/.gitmodules b/.gitmodules index 7098d9d08..955377530 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,9 @@ [submodule "OTRExporter"] path = OTRExporter url = https://github.com/harbourmasters/OTRExporter +[submodule "subprojects/wswrap"] + path = subprojects/wswrap + url = https://github.com/black-sliver/wswrap.git +[submodule "subprojects/apclientpp"] + path = subprojects/apclientpp + url = https://github.com/black-sliver/apclientpp.git diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 7277b2dfb..90fa3c363 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -300,6 +300,23 @@ else() message("Boost found in " ${Boost_INCLUDE_DIRS}) set(BOOST-INCLUDE ${Boost_INCLUDE_DIRS}) endif() + +################################################################################ +# apclientpp + dependencies +################################################################################ +add_compile_definitions(ASIO_STANDALONE) + +# TEMPORARY +add_compile_definitions(WSWRAP_NO_SSL) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../subprojects/asio/include + ${CMAKE_CURRENT_SOURCE_DIR}/../subprojects/websocketpp + ${CMAKE_CURRENT_SOURCE_DIR}/../subprojects/wswrap/include + ${CMAKE_CURRENT_SOURCE_DIR}/../subprojects/valijson/include + ${CMAKE_CURRENT_SOURCE_DIR}/../subprojects/apclientpp +) + ################################################################################ # Compile definitions ################################################################################ diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp new file mode 100644 index 000000000..f322ee85c --- /dev/null +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -0,0 +1,2 @@ +#include "Archipelago.h" +#include diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -0,0 +1 @@ + diff --git a/subprojects/apclientpp b/subprojects/apclientpp new file mode 160000 index 000000000..4ab7ba634 --- /dev/null +++ b/subprojects/apclientpp @@ -0,0 +1 @@ +Subproject commit 4ab7ba634809459196f476d3a485ddb076d62565 diff --git a/subprojects/asio/COPYING b/subprojects/asio/COPYING new file mode 100644 index 000000000..532cec6c8 --- /dev/null +++ b/subprojects/asio/COPYING @@ -0,0 +1,4 @@ +Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/subprojects/asio/LICENSE_1_0.txt b/subprojects/asio/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/subprojects/asio/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/subprojects/asio/include/asio.hpp b/subprojects/asio/include/asio.hpp new file mode 100644 index 000000000..3356df009 --- /dev/null +++ b/subprojects/asio/include/asio.hpp @@ -0,0 +1,152 @@ +// +// asio.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HPP +#define ASIO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/async_result.hpp" +#include "asio/basic_datagram_socket.hpp" +#include "asio/basic_deadline_timer.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/basic_raw_socket.hpp" +#include "asio/basic_seq_packet_socket.hpp" +#include "asio/basic_serial_port.hpp" +#include "asio/basic_signal_set.hpp" +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_socket_streambuf.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/basic_streambuf.hpp" +#include "asio/basic_waitable_timer.hpp" +#include "asio/bind_executor.hpp" +#include "asio/buffer.hpp" +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/buffered_stream.hpp" +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/buffers_iterator.hpp" +#include "asio/completion_condition.hpp" +#include "asio/connect.hpp" +#include "asio/coroutine.hpp" +#include "asio/datagram_socket_service.hpp" +#include "asio/deadline_timer_service.hpp" +#include "asio/deadline_timer.hpp" +#include "asio/defer.hpp" +#include "asio/dispatch.hpp" +#include "asio/error.hpp" +#include "asio/error_code.hpp" +#include "asio/execution_context.hpp" +#include "asio/executor.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/generic/basic_endpoint.hpp" +#include "asio/generic/datagram_protocol.hpp" +#include "asio/generic/raw_protocol.hpp" +#include "asio/generic/seq_packet_protocol.hpp" +#include "asio/generic/stream_protocol.hpp" +#include "asio/handler_alloc_hook.hpp" +#include "asio/handler_continuation_hook.hpp" +#include "asio/handler_invoke_hook.hpp" +#include "asio/handler_type.hpp" +#include "asio/high_resolution_timer.hpp" +#include "asio/io_context.hpp" +#include "asio/io_context_strand.hpp" +#include "asio/io_service.hpp" +#include "asio/io_service_strand.hpp" +#include "asio/ip/address.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v4_iterator.hpp" +#include "asio/ip/address_v4_range.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/ip/address_v6_iterator.hpp" +#include "asio/ip/address_v6_range.hpp" +#include "asio/ip/bad_address_cast.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_entry.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/host_name.hpp" +#include "asio/ip/icmp.hpp" +#include "asio/ip/multicast.hpp" +#include "asio/ip/resolver_base.hpp" +#include "asio/ip/resolver_query_base.hpp" +#include "asio/ip/resolver_service.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/ip/udp.hpp" +#include "asio/ip/unicast.hpp" +#include "asio/ip/v6_only.hpp" +#include "asio/is_executor.hpp" +#include "asio/is_read_buffered.hpp" +#include "asio/is_write_buffered.hpp" +#include "asio/local/basic_endpoint.hpp" +#include "asio/local/connect_pair.hpp" +#include "asio/local/datagram_protocol.hpp" +#include "asio/local/stream_protocol.hpp" +#include "asio/packaged_task.hpp" +#include "asio/placeholders.hpp" +#include "asio/posix/basic_descriptor.hpp" +#include "asio/posix/basic_stream_descriptor.hpp" +#include "asio/posix/descriptor.hpp" +#include "asio/posix/descriptor_base.hpp" +#include "asio/posix/stream_descriptor.hpp" +#include "asio/posix/stream_descriptor_service.hpp" +#include "asio/post.hpp" +#include "asio/raw_socket_service.hpp" +#include "asio/read.hpp" +#include "asio/read_at.hpp" +#include "asio/read_until.hpp" +#include "asio/seq_packet_socket_service.hpp" +#include "asio/serial_port.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/serial_port_service.hpp" +#include "asio/signal_set.hpp" +#include "asio/signal_set_service.hpp" +#include "asio/socket_acceptor_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/steady_timer.hpp" +#include "asio/strand.hpp" +#include "asio/stream_socket_service.hpp" +#include "asio/streambuf.hpp" +#include "asio/system_context.hpp" +#include "asio/system_error.hpp" +#include "asio/system_executor.hpp" +#include "asio/system_timer.hpp" +#include "asio/thread.hpp" +#include "asio/thread_pool.hpp" +#include "asio/time_traits.hpp" +#include "asio/use_future.hpp" +#include "asio/uses_executor.hpp" +#include "asio/version.hpp" +#include "asio/wait_traits.hpp" +#include "asio/waitable_timer_service.hpp" +#include "asio/windows/basic_handle.hpp" +#include "asio/windows/basic_object_handle.hpp" +#include "asio/windows/basic_random_access_handle.hpp" +#include "asio/windows/basic_stream_handle.hpp" +#include "asio/windows/object_handle.hpp" +#include "asio/windows/object_handle_service.hpp" +#include "asio/windows/overlapped_handle.hpp" +#include "asio/windows/overlapped_ptr.hpp" +#include "asio/windows/random_access_handle.hpp" +#include "asio/windows/random_access_handle_service.hpp" +#include "asio/windows/stream_handle.hpp" +#include "asio/windows/stream_handle_service.hpp" +#include "asio/write.hpp" +#include "asio/write_at.hpp" + +#endif // ASIO_HPP diff --git a/subprojects/asio/include/asio/COPYING b/subprojects/asio/include/asio/COPYING new file mode 100644 index 000000000..532cec6c8 --- /dev/null +++ b/subprojects/asio/include/asio/COPYING @@ -0,0 +1,4 @@ +Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/subprojects/asio/include/asio/LICENSE_1_0.txt b/subprojects/asio/include/asio/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/subprojects/asio/include/asio/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/subprojects/asio/include/asio/associated_allocator.hpp b/subprojects/asio/include/asio/associated_allocator.hpp new file mode 100644 index 000000000..8b488bb34 --- /dev/null +++ b/subprojects/asio/include/asio/associated_allocator.hpp @@ -0,0 +1,131 @@ +// +// associated_allocator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ASSOCIATED_ALLOCATOR_HPP +#define ASIO_ASSOCIATED_ALLOCATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct associated_allocator_check +{ + typedef void type; +}; + +template +struct associated_allocator_impl +{ + typedef E type; + + static type get(const T&, const E& e) ASIO_NOEXCEPT + { + return e; + } +}; + +template +struct associated_allocator_impl::type> +{ + typedef typename T::allocator_type type; + + static type get(const T& t, const E&) ASIO_NOEXCEPT + { + return t.get_allocator(); + } +}; + +} // namespace detail + +/// Traits type used to obtain the allocator associated with an object. +/** + * A program may specialise this traits type if the @c T template parameter in + * the specialisation is a user-defined type. The template parameter @c + * Allocator shall be a type meeting the Allocator requirements. + * + * Specialisations shall meet the following requirements, where @c t is a const + * reference to an object of type @c T, and @c a is an object of type @c + * Allocator. + * + * @li Provide a nested typedef @c type that identifies a type meeting the + * Allocator requirements. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t) and with return type @c type. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t,a) and with return type @c type. + */ +template > +struct associated_allocator +{ + /// If @c T has a nested type @c allocator_type, T::allocator_type. + /// Otherwise @c Allocator. +#if defined(GENERATING_DOCUMENTATION) + typedef see_below type; +#else // defined(GENERATING_DOCUMENTATION) + typedef typename detail::associated_allocator_impl::type type; +#endif // defined(GENERATING_DOCUMENTATION) + + /// If @c T has a nested type @c allocator_type, returns + /// t.get_allocator(). Otherwise returns @c a. + static type get(const T& t, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return detail::associated_allocator_impl::get(t, a); + } +}; + +/// Helper function to obtain an object's associated allocator. +/** + * @returns associated_allocator::get(t) + */ +template +inline typename associated_allocator::type +get_associated_allocator(const T& t) ASIO_NOEXCEPT +{ + return associated_allocator::get(t); +} + +/// Helper function to obtain an object's associated allocator. +/** + * @returns associated_allocator::get(t, a) + */ +template +inline typename associated_allocator::type +get_associated_allocator(const T& t, const Allocator& a) ASIO_NOEXCEPT +{ + return associated_allocator::get(t, a); +} + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template > +using associated_allocator_t + = typename associated_allocator::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ASSOCIATED_ALLOCATOR_HPP diff --git a/subprojects/asio/include/asio/associated_executor.hpp b/subprojects/asio/include/asio/associated_executor.hpp new file mode 100644 index 000000000..4c5c20788 --- /dev/null +++ b/subprojects/asio/include/asio/associated_executor.hpp @@ -0,0 +1,149 @@ +// +// associated_executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ASSOCIATED_EXECUTOR_HPP +#define ASIO_ASSOCIATED_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/is_executor.hpp" +#include "asio/system_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct associated_executor_check +{ + typedef void type; +}; + +template +struct associated_executor_impl +{ + typedef E type; + + static type get(const T&, const E& e) ASIO_NOEXCEPT + { + return e; + } +}; + +template +struct associated_executor_impl::type> +{ + typedef typename T::executor_type type; + + static type get(const T& t, const E&) ASIO_NOEXCEPT + { + return t.get_executor(); + } +}; + +} // namespace detail + +/// Traits type used to obtain the executor associated with an object. +/** + * A program may specialise this traits type if the @c T template parameter in + * the specialisation is a user-defined type. The template parameter @c + * Executor shall be a type meeting the Executor requirements. + * + * Specialisations shall meet the following requirements, where @c t is a const + * reference to an object of type @c T, and @c e is an object of type @c + * Executor. + * + * @li Provide a nested typedef @c type that identifies a type meeting the + * Executor requirements. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t) and with return type @c type. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t,e) and with return type @c type. + */ +template +struct associated_executor +{ + /// If @c T has a nested type @c executor_type, T::executor_type. + /// Otherwise @c Executor. +#if defined(GENERATING_DOCUMENTATION) + typedef see_below type; +#else // defined(GENERATING_DOCUMENTATION) + typedef typename detail::associated_executor_impl::type type; +#endif // defined(GENERATING_DOCUMENTATION) + + /// If @c T has a nested type @c executor_type, returns + /// t.get_executor(). Otherwise returns @c ex. + static type get(const T& t, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return detail::associated_executor_impl::get(t, ex); + } +}; + +/// Helper function to obtain an object's associated executor. +/** + * @returns associated_executor::get(t) + */ +template +inline typename associated_executor::type +get_associated_executor(const T& t) ASIO_NOEXCEPT +{ + return associated_executor::get(t); +} + +/// Helper function to obtain an object's associated executor. +/** + * @returns associated_executor::get(t, ex) + */ +template +inline typename associated_executor::type +get_associated_executor(const T& t, const Executor& ex, + typename enable_if::value>::type* = 0) ASIO_NOEXCEPT +{ + return associated_executor::get(t, ex); +} + +/// Helper function to obtain an object's associated executor. +/** + * @returns associated_executor::get(t, ctx.get_executor()) + */ +template +inline typename associated_executor::type +get_associated_executor(const T& t, ExecutionContext& ctx, + typename enable_if::value>::type* = 0) ASIO_NOEXCEPT +{ + return associated_executor::get(t, ctx.get_executor()); +} + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template +using associated_executor_t = typename associated_executor::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ASSOCIATED_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/async_result.hpp b/subprojects/asio/include/asio/async_result.hpp new file mode 100644 index 000000000..18acdf20a --- /dev/null +++ b/subprojects/asio/include/asio/async_result.hpp @@ -0,0 +1,221 @@ +// +// async_result.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ASYNC_RESULT_HPP +#define ASIO_ASYNC_RESULT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/handler_type.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// An interface for customising the behaviour of an initiating function. +/** + * The async_result traits class is used for determining: + * + * @li the concrete completion handler type to be called at the end of the + * asynchronous operation; + * + * @li the initiating function return type; and + * + * @li how the return value of the initiating function is obtained. + * + * The trait allows the handler and return types to be determined at the point + * where the specific completion handler signature is known. + * + * This template may be specialised for user-defined completion token types. + * The primary template assumes that the CompletionToken is the completion + * handler. + */ +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +template +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +template +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +class async_result +{ +public: +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + /// The concrete completion handler type for the specific signature. + typedef CompletionToken completion_handler_type; + + /// The return type of the initiating function. + typedef void return_type; +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + // For backward compatibility, determine the concrete completion handler type + // by using the legacy handler_type trait. + typedef typename handler_type::type + completion_handler_type; + + // For backward compatibility, determine the initiating function return type + // using the legacy single-parameter version of async_result. + typedef typename async_result::type return_type; +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + + /// Construct an async result from a given handler. + /** + * When using a specalised async_result, the constructor has an opportunity + * to initialise some state associated with the completion handler, which is + * then returned from the initiating function. + */ + explicit async_result(completion_handler_type& h) +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + // No data members to initialise. +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + : legacy_result_(h) +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + { + (void)h; + } + + /// Obtain the value to be returned from the initiating function. + return_type get() + { +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + // Nothing to do. +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + return legacy_result_.get(); +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + } + +private: + async_result(const async_result&) ASIO_DELETED; + async_result& operator=(const async_result&) ASIO_DELETED; + +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + // No data members. +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + async_result legacy_result_; +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +}; + +#if !defined(ASIO_NO_DEPRECATED) + +/// (Deprecated: Use two-parameter version of async_result.) An interface for +/// customising the behaviour of an initiating function. +/** + * This template may be specialised for user-defined handler types. + */ +template +class async_result +{ +public: + /// The return type of the initiating function. + typedef void type; + + /// Construct an async result from a given handler. + /** + * When using a specalised async_result, the constructor has an opportunity + * to initialise some state associated with the handler, which is then + * returned from the initiating function. + */ + explicit async_result(Handler&) + { + } + + /// Obtain the value to be returned from the initiating function. + type get() + { + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Helper template to deduce the handler type from a CompletionToken, capture +/// a local copy of the handler, and then create an async_result for the +/// handler. +template +struct async_completion +{ + /// The real handler type to be used for the asynchronous operation. + typedef typename asio::async_result< + typename decay::type, + Signature>::completion_handler_type completion_handler_type; + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Constructor. + /** + * The constructor creates the concrete completion handler and makes the link + * between the handler and the asynchronous result. + */ + explicit async_completion(CompletionToken& token) + : completion_handler(static_cast::value, + completion_handler_type&, CompletionToken&&>::type>(token)), + result(completion_handler) + { + } +#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + explicit async_completion(typename decay::type& token) + : completion_handler(token), + result(completion_handler) + { + } + + explicit async_completion(const typename decay::type& token) + : completion_handler(token), + result(completion_handler) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// A copy of, or reference to, a real handler object. +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + typename conditional< + is_same::value, + completion_handler_type&, completion_handler_type>::type completion_handler; +#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + completion_handler_type completion_handler; +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// The result of the asynchronous operation's initiating function. + async_result::type, Signature> result; +}; + +namespace detail { + +template +struct async_result_helper + : async_result::type, Signature> +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + void_or_deduced +#elif defined(_MSC_VER) && (_MSC_VER < 1500) +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + typename ::asio::detail::async_result_helper< \ + ct, sig>::return_type +#define ASIO_HANDLER_TYPE(ct, sig) \ + typename ::asio::detail::async_result_helper< \ + ct, sig>::completion_handler_type +#else +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + typename ::asio::async_result< \ + typename ::asio::decay::type, sig>::return_type +#define ASIO_HANDLER_TYPE(ct, sig) \ + typename ::asio::async_result< \ + typename ::asio::decay::type, sig>::completion_handler_type +#endif + +#endif // ASIO_ASYNC_RESULT_HPP diff --git a/subprojects/asio/include/asio/basic_datagram_socket.hpp b/subprojects/asio/include/asio/basic_datagram_socket.hpp new file mode 100644 index 000000000..346cc353f --- /dev/null +++ b/subprojects/asio/include/asio/basic_datagram_socket.hpp @@ -0,0 +1,1040 @@ +// +// basic_datagram_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP +#define ASIO_BASIC_DATAGRAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/datagram_socket_service.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides datagram-oriented socket functionality. +/** + * The basic_datagram_socket class template provides asynchronous and blocking + * datagram-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template )> +class basic_datagram_socket + : public basic_socket +{ +public: + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket< + Protocol ASIO_SVC_TARG>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param io_context The io_context object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + */ + explicit basic_datagram_socket(asio::io_context& io_context) + : basic_socket(io_context) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param io_context The io_context object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(asio::io_context& io_context, + const protocol_type& protocol) + : basic_socket(io_context, protocol) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_context The io_context object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(asio::io_context& io_context, + const endpoint_type& endpoint) + : basic_socket(io_context, endpoint) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param io_context The io_context object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(asio::io_context& io_context, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket( + io_context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_datagram_socket from another. + /** + * This constructor moves a datagram socket from one object to another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(io_context&) constructor. + */ + basic_datagram_socket(basic_datagram_socket&& other) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_datagram_socket from another. + /** + * This assignment operator moves a datagram socket from one object to + * another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(io_context&) constructor. + */ + basic_datagram_socket& operator=(basic_datagram_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_datagram_socket from a socket of another protocol + /// type. + /** + * This constructor moves a datagram socket from one object to another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(io_context&) constructor. + */ + template + basic_datagram_socket( + basic_datagram_socket&& other, + typename enable_if::value>::type* = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_datagram_socket from a socket of another protocol + /// type. + /** + * This assignment operator moves a datagram socket from one object to + * another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(io_context&) constructor. + */ + template + typename enable_if::value, + basic_datagram_socket>::type& operator=( + basic_datagram_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_datagram_socket() + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->get_service().send( + this->get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to asynchronously send data on the datagram socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send(this->get_implementation(), + buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send(this->get_implementation(), + buffers, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to asynchronously send data on the datagram socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send(this->get_implementation(), + buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send(this->get_implementation(), + buffers, flags, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination) + { + asio::error_code ec; + std::size_t s = this->get_service().send_to( + this->get_implementation(), buffers, destination, 0, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().send_to( + this->get_implementation(), buffers, destination, flags, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->get_service().send_to(this->get_implementation(), + buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send_to( + this->get_implementation(), buffers, destination, 0, + ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send_to( + this->get_implementation(), buffers, destination, 0, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send_to( + this->get_implementation(), buffers, destination, flags, + ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send_to( + this->get_implementation(), buffers, destination, flags, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->get_service().receive( + this->get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive(this->get_implementation(), + buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive(this->get_implementation(), + buffers, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive(this->get_implementation(), + buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive(this->get_implementation(), + buffers, flags, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint) + { + asio::error_code ec; + std::size_t s = this->get_service().receive_from( + this->get_implementation(), buffers, sender_endpoint, 0, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().receive_from( + this->get_implementation(), buffers, sender_endpoint, flags, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->get_service().receive_from(this->get_implementation(), + buffers, sender_endpoint, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * asio::buffer(data, size), sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, 0, + ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, 0, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, flags, + ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, flags, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP diff --git a/subprojects/asio/include/asio/basic_deadline_timer.hpp b/subprojects/asio/include/asio/basic_deadline_timer.hpp new file mode 100644 index 000000000..5b20066df --- /dev/null +++ b/subprojects/asio/include/asio/basic_deadline_timer.hpp @@ -0,0 +1,628 @@ +// +// basic_deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP +#define ASIO_BASIC_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/time_traits.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/deadline_timer_service.hpp" +#else // defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/detail/deadline_timer_service.hpp" +# define ASIO_SVC_T detail::deadline_timer_service +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides waitable timer functionality. +/** + * The basic_deadline_timer class template provides the ability to perform a + * blocking or asynchronous wait for a timer to expire. + * + * A deadline timer is always in one of two states: "expired" or "not expired". + * If the wait() or async_wait() function is called on an expired timer, the + * wait operation will complete immediately. + * + * Most applications will use the asio::deadline_timer typedef. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Examples + * Performing a blocking wait: + * @code + * // Construct a timer without setting an expiry time. + * asio::deadline_timer timer(io_context); + * + * // Set an expiry time relative to now. + * timer.expires_from_now(boost::posix_time::seconds(5)); + * + * // Wait for the timer to expire. + * timer.wait(); + * @endcode + * + * @par + * Performing an asynchronous wait: + * @code + * void handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Timer expired. + * } + * } + * + * ... + * + * // Construct a timer with an absolute expiry time. + * asio::deadline_timer timer(io_context, + * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); + * + * // Start an asynchronous wait. + * timer.async_wait(handler); + * @endcode + * + * @par Changing an active deadline_timer's expiry time + * + * Changing the expiry time of a timer while there are pending asynchronous + * waits causes those wait operations to be cancelled. To ensure that the action + * associated with the timer is performed only once, use something like this: + * used: + * + * @code + * void on_some_event() + * { + * if (my_timer.expires_from_now(seconds(5)) > 0) + * { + * // We managed to cancel the timer. Start new asynchronous wait. + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const asio::error_code& e) + * { + * if (e != asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @li The asio::basic_deadline_timer::expires_from_now() function + * cancels any pending asynchronous waits, and returns the number of + * asynchronous waits that were cancelled. If it returns 0 then you were too + * late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the asio::error_code passed to + * it contains the value asio::error::operation_aborted. + */ +template + ASIO_SVC_TPARAM_DEF2(= deadline_timer_service)> +class basic_deadline_timer + : ASIO_SVC_ACCESS basic_io_object +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The time traits type. + typedef TimeTraits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_from_now() functions must be called to set an + * expiry time before the timer can be waited on. + * + * @param io_context The io_context object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + */ + explicit basic_deadline_timer(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_context The io_context object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + basic_deadline_timer(asio::io_context& io_context, + const time_type& expiry_time) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().expires_at(this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_context The io_context object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + basic_deadline_timer(asio::io_context& io_context, + const duration_type& expiry_time) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().expires_from_now( + this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_deadline_timer from another. + /** + * This constructor moves a timer from one object to another. + * + * @param other The other basic_deadline_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_deadline_timer(io_context&) constructor. + */ + basic_deadline_timer(basic_deadline_timer&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign a basic_deadline_timer from another. + /** + * This assignment operator moves a timer from one object to another. Cancels + * any outstanding asynchronous operations associated with the target object. + * + * @param other The other basic_deadline_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_deadline_timer(io_context&) constructor. + */ + basic_deadline_timer& operator=(basic_deadline_timer&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the timer. + /** + * This function destroys the timer, cancelling any outstanding asynchronous + * wait operations associated with the timer as if by calling @c cancel. + */ + ~basic_deadline_timer() + { + } + +#if defined(ASIO_ENABLE_OLD_SERVICES) + // These functions are provided by basic_io_object<>. +#else // defined(ASIO_ENABLE_OLD_SERVICES) +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel() + { + asio::error_code ec; + std::size_t s = this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + return s; + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel(asio::error_code& ec) + { + return this->get_service().cancel(this->get_implementation(), ec); + } + + /// Cancels one asynchronous operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one() + { + asio::error_code ec; + std::size_t s = this->get_service().cancel_one( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel_one"); + return s; + } + + /// Cancels one asynchronous operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one(asio::error_code& ec) + { + return this->get_service().cancel_one(this->get_implementation(), ec); + } + + /// Get the timer's expiry time as an absolute time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_type expires_at() const + { + return this->get_service().expires_at(this->get_implementation()); + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_type& expiry_time) + { + asio::error_code ec; + std::size_t s = this->get_service().expires_at( + this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + return s; + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_type& expiry_time, + asio::error_code& ec) + { + return this->get_service().expires_at( + this->get_implementation(), expiry_time, ec); + } + + /// Get the timer's expiry time relative to now. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + duration_type expires_from_now() const + { + return this->get_service().expires_from_now(this->get_implementation()); + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration_type& expiry_time) + { + asio::error_code ec; + std::size_t s = this->get_service().expires_from_now( + this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + return s; + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration_type& expiry_time, + asio::error_code& ec) + { + return this->get_service().expires_from_now( + this->get_implementation(), expiry_time, ec); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @throws asio::system_error Thrown on failure. + */ + void wait() + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @param ec Set to indicate what error occurred, if any. + */ + void wait(asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), ec); + } + + /// Start an asynchronous wait on the timer. + /** + * This function may be used to initiate an asynchronous wait against the + * timer. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li The timer has expired. + * + * @li The timer was cancelled, in which case the handler is passed the error + * code asio::error::operation_aborted. + * + * @param handler The handler to be called when the timer expires. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(ASIO_MOVE_ARG(WaitHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_wait(this->get_implementation(), + ASIO_MOVE_CAST(WaitHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_wait(this->get_implementation(), + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# undef ASIO_SVC_T +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_BASIC_DEADLINE_TIMER_HPP diff --git a/subprojects/asio/include/asio/basic_io_object.hpp b/subprojects/asio/include/asio/basic_io_object.hpp new file mode 100644 index 000000000..442e85429 --- /dev/null +++ b/subprojects/asio/include/asio/basic_io_object.hpp @@ -0,0 +1,290 @@ +// +// basic_io_object.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_IO_OBJECT_HPP +#define ASIO_BASIC_IO_OBJECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_MOVE) +namespace detail +{ + // Type trait used to determine whether a service supports move. + template + class service_has_move + { + private: + typedef IoObjectService service_type; + typedef typename service_type::implementation_type implementation_type; + + template + static auto asio_service_has_move_eval(T* t, U* u) + -> decltype(t->move_construct(*u, *u), char()); + static char (&asio_service_has_move_eval(...))[2]; + + public: + static const bool value = + sizeof(asio_service_has_move_eval( + static_cast(0), + static_cast(0))) == 1; + }; +} +#endif // defined(ASIO_HAS_MOVE) + +/// Base class for all I/O objects. +/** + * @note All I/O objects are non-copyable. However, when using C++0x, certain + * I/O objects do support move construction and move assignment. + */ +#if !defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) +template +#else +template ::value> +#endif +class basic_io_object +{ +public: + /// The type of the service that will be used to provide I/O operations. + typedef IoObjectService service_type; + + /// The underlying implementation type of I/O object. + typedef typename service_type::implementation_type implementation_type; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return service_.get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return service_.get_io_context(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// The type of the executor associated with the object. + typedef asio::io_context::executor_type executor_type; + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return service_.get_io_context().get_executor(); + } + +protected: + /// Construct a basic_io_object. + /** + * Performs: + * @code get_service().construct(get_implementation()); @endcode + */ + explicit basic_io_object(asio::io_context& io_context) + : service_(asio::use_service(io_context)) + { + service_.construct(implementation_); + } + +#if defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_io_object. + /** + * Performs: + * @code get_service().move_construct( + * get_implementation(), other.get_implementation()); @endcode + * + * @note Available only for services that support movability, + */ + basic_io_object(basic_io_object&& other); + + /// Move-assign a basic_io_object. + /** + * Performs: + * @code get_service().move_assign(get_implementation(), + * other.get_service(), other.get_implementation()); @endcode + * + * @note Available only for services that support movability, + */ + basic_io_object& operator=(basic_io_object&& other); + + /// Perform a converting move-construction of a basic_io_object. + template + basic_io_object(IoObjectService1& other_service, + typename IoObjectService1::implementation_type& other_implementation); +#endif // defined(GENERATING_DOCUMENTATION) + + /// Protected destructor to prevent deletion through this type. + /** + * Performs: + * @code get_service().destroy(get_implementation()); @endcode + */ + ~basic_io_object() + { + service_.destroy(implementation_); + } + + /// Get the service associated with the I/O object. + service_type& get_service() + { + return service_; + } + + /// Get the service associated with the I/O object. + const service_type& get_service() const + { + return service_; + } + + /// Get the underlying implementation of the I/O object. + implementation_type& get_implementation() + { + return implementation_; + } + + /// Get the underlying implementation of the I/O object. + const implementation_type& get_implementation() const + { + return implementation_; + } + +private: + basic_io_object(const basic_io_object&); + basic_io_object& operator=(const basic_io_object&); + + // The service associated with the I/O object. + service_type& service_; + + /// The underlying implementation of the I/O object. + implementation_type implementation_; +}; + +#if defined(ASIO_HAS_MOVE) +// Specialisation for movable objects. +template +class basic_io_object +{ +public: + typedef IoObjectService service_type; + typedef typename service_type::implementation_type implementation_type; + +#if !defined(ASIO_NO_DEPRECATED) + asio::io_context& get_io_context() + { + return service_->get_io_context(); + } + + asio::io_context& get_io_service() + { + return service_->get_io_context(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + typedef asio::io_context::executor_type executor_type; + + executor_type get_executor() ASIO_NOEXCEPT + { + return service_->get_io_context().get_executor(); + } + +protected: + explicit basic_io_object(asio::io_context& io_context) + : service_(&asio::use_service(io_context)) + { + service_->construct(implementation_); + } + + basic_io_object(basic_io_object&& other) + : service_(&other.get_service()) + { + service_->move_construct(implementation_, other.implementation_); + } + + template + basic_io_object(IoObjectService1& other_service, + typename IoObjectService1::implementation_type& other_implementation) + : service_(&asio::use_service( + other_service.get_io_context())) + { + service_->converting_move_construct(implementation_, + other_service, other_implementation); + } + + ~basic_io_object() + { + service_->destroy(implementation_); + } + + basic_io_object& operator=(basic_io_object&& other) + { + service_->move_assign(implementation_, + *other.service_, other.implementation_); + service_ = other.service_; + return *this; + } + + service_type& get_service() + { + return *service_; + } + + const service_type& get_service() const + { + return *service_; + } + + implementation_type& get_implementation() + { + return implementation_; + } + + const implementation_type& get_implementation() const + { + return implementation_; + } + +private: + basic_io_object(const basic_io_object&); + void operator=(const basic_io_object&); + + IoObjectService* service_; + implementation_type implementation_; +}; +#endif // defined(ASIO_HAS_MOVE) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_IO_OBJECT_HPP diff --git a/subprojects/asio/include/asio/basic_raw_socket.hpp b/subprojects/asio/include/asio/basic_raw_socket.hpp new file mode 100644 index 000000000..0de7c7766 --- /dev/null +++ b/subprojects/asio/include/asio/basic_raw_socket.hpp @@ -0,0 +1,1030 @@ +// +// basic_raw_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_RAW_SOCKET_HPP +#define ASIO_BASIC_RAW_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/raw_socket_service.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides raw-oriented socket functionality. +/** + * The basic_raw_socket class template provides asynchronous and blocking + * raw-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template )> +class basic_raw_socket + : public basic_socket +{ +public: + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket< + Protocol ASIO_SVC_TARG>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_raw_socket without opening it. + /** + * This constructor creates a raw socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param io_context The io_context object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + */ + explicit basic_raw_socket(asio::io_context& io_context) + : basic_socket(io_context) + { + } + + /// Construct and open a basic_raw_socket. + /** + * This constructor creates and opens a raw socket. + * + * @param io_context The io_context object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(asio::io_context& io_context, + const protocol_type& protocol) + : basic_socket(io_context, protocol) + { + } + + /// Construct a basic_raw_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a raw socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_context The io_context object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param endpoint An endpoint on the local machine to which the raw + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(asio::io_context& io_context, + const endpoint_type& endpoint) + : basic_socket(io_context, endpoint) + { + } + + /// Construct a basic_raw_socket on an existing native socket. + /** + * This constructor creates a raw socket object to hold an existing + * native socket. + * + * @param io_context The io_context object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(asio::io_context& io_context, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket( + io_context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_raw_socket from another. + /** + * This constructor moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(io_context&) constructor. + */ + basic_raw_socket(basic_raw_socket&& other) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_raw_socket from another. + /** + * This assignment operator moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(io_context&) constructor. + */ + basic_raw_socket& operator=(basic_raw_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_raw_socket from a socket of another protocol type. + /** + * This constructor moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(io_context&) constructor. + */ + template + basic_raw_socket(basic_raw_socket&& other, + typename enable_if::value>::type* = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_raw_socket from a socket of another protocol type. + /** + * This assignment operator moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(io_context&) constructor. + */ + template + typename enable_if::value, + basic_raw_socket>::type& operator=( + basic_raw_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_raw_socket() + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->get_service().send( + this->get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected raw + * socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send(this->get_implementation(), + buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send(this->get_implementation(), + buffers, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected raw + * socket. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send(this->get_implementation(), + buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send(this->get_implementation(), + buffers, flags, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination) + { + asio::error_code ec; + std::size_t s = this->get_service().send_to( + this->get_implementation(), buffers, destination, 0, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().send_to( + this->get_implementation(), buffers, destination, flags, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->get_service().send_to(this->get_implementation(), + buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send raw data to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send_to(this->get_implementation(), + buffers, destination, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send_to(this->get_implementation(), + buffers, destination, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send raw data to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send_to( + this->get_implementation(), buffers, destination, flags, + ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send_to( + this->get_implementation(), buffers, destination, flags, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->get_service().receive( + this->get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the raw + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * raw socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive(this->get_implementation(), + buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive(this->get_implementation(), + buffers, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the raw + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * raw socket. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive(this->get_implementation(), + buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive(this->get_implementation(), + buffers, flags, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint) + { + asio::error_code ec; + std::size_t s = this->get_service().receive_from( + this->get_implementation(), buffers, sender_endpoint, 0, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().receive_from( + this->get_implementation(), buffers, sender_endpoint, flags, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->get_service().receive_from(this->get_implementation(), + buffers, sender_endpoint, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive raw data. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, 0, + ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, 0, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive raw data. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, flags, + ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive_from( + this->get_implementation(), buffers, sender_endpoint, flags, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_RAW_SOCKET_HPP diff --git a/subprojects/asio/include/asio/basic_seq_packet_socket.hpp b/subprojects/asio/include/asio/basic_seq_packet_socket.hpp new file mode 100644 index 000000000..3655d881c --- /dev/null +++ b/subprojects/asio/include/asio/basic_seq_packet_socket.hpp @@ -0,0 +1,618 @@ +// +// basic_seq_packet_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SEQ_PACKET_SOCKET_HPP +#define ASIO_BASIC_SEQ_PACKET_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/seq_packet_socket_service.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides sequenced packet socket functionality. +/** + * The basic_seq_packet_socket class template provides asynchronous and blocking + * sequenced packet socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template )> +class basic_seq_packet_socket + : public basic_socket +{ +public: + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket< + Protocol ASIO_SVC_TARG>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_seq_packet_socket without opening it. + /** + * This constructor creates a sequenced packet socket without opening it. The + * socket needs to be opened and then connected or accepted before data can + * be sent or received on it. + * + * @param io_context The io_context object that the sequenced packet socket + * will use to dispatch handlers for any asynchronous operations performed on + * the socket. + */ + explicit basic_seq_packet_socket(asio::io_context& io_context) + : basic_socket(io_context) + { + } + + /// Construct and open a basic_seq_packet_socket. + /** + * This constructor creates and opens a sequenced_packet socket. The socket + * needs to be connected or accepted before data can be sent or received on + * it. + * + * @param io_context The io_context object that the sequenced packet socket + * will use to dispatch handlers for any asynchronous operations performed on + * the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_seq_packet_socket(asio::io_context& io_context, + const protocol_type& protocol) + : basic_socket(io_context, protocol) + { + } + + /// Construct a basic_seq_packet_socket, opening it and binding it to the + /// given local endpoint. + /** + * This constructor creates a sequenced packet socket and automatically opens + * it bound to the specified endpoint on the local machine. The protocol used + * is the protocol associated with the given endpoint. + * + * @param io_context The io_context object that the sequenced packet socket + * will use to dispatch handlers for any asynchronous operations performed on + * the socket. + * + * @param endpoint An endpoint on the local machine to which the sequenced + * packet socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_seq_packet_socket(asio::io_context& io_context, + const endpoint_type& endpoint) + : basic_socket(io_context, endpoint) + { + } + + /// Construct a basic_seq_packet_socket on an existing native socket. + /** + * This constructor creates a sequenced packet socket object to hold an + * existing native socket. + * + * @param io_context The io_context object that the sequenced packet socket + * will use to dispatch handlers for any asynchronous operations performed on + * the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_seq_packet_socket(asio::io_context& io_context, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket( + io_context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_seq_packet_socket from another. + /** + * This constructor moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + */ + basic_seq_packet_socket(basic_seq_packet_socket&& other) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_seq_packet_socket from another. + /** + * This assignment operator moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + */ + basic_seq_packet_socket& operator=(basic_seq_packet_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_seq_packet_socket from a socket of another protocol + /// type. + /** + * This constructor moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + */ + template + basic_seq_packet_socket( + basic_seq_packet_socket&& other, + typename enable_if::value>::type* = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_seq_packet_socket from a socket of another protocol + /// type. + /** + * This assignment operator moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + */ + template + typename enable_if::value, + basic_seq_packet_socket>::type& operator=( + basic_seq_packet_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_seq_packet_socket() + { + } + + /// Send some data on the socket. + /** + * This function is used to send data on the sequenced packet socket. The + * function call will block until the data has been sent successfully, or an + * until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the sequenced packet socket. The + * function call will block the data has been sent successfully, or an until + * error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. Returns 0 if an error occurred. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->get_service().send( + this->get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the sequenced packet + * socket. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send(this->get_implementation(), + buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send(this->get_implementation(), + buffers, flags, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the sequenced packet socket. The + * function call will block until data has been received successfully, or + * until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param out_flags After the receive call completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), out_flags); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags& out_flags) + { + asio::error_code ec; +#if defined(ASIO_ENABLE_OLD_SERVICES) + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, 0, out_flags, ec); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + std::size_t s = this->get_service().receive_with_flags( + this->get_implementation(), buffers, 0, out_flags, ec); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the sequenced packet socket. The + * function call will block until data has been received successfully, or + * until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param in_flags Flags specifying how the receive call is to be made. + * + * @param out_flags After the receive call completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), 0, out_flags); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags) + { + asio::error_code ec; +#if defined(ASIO_ENABLE_OLD_SERVICES) + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, in_flags, out_flags, ec); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + std::size_t s = this->get_service().receive_with_flags( + this->get_implementation(), buffers, in_flags, out_flags, ec); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the sequenced packet socket. The + * function call will block until data has been received successfully, or + * until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param in_flags Flags specifying how the receive call is to be made. + * + * @param out_flags After the receive call completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. Returns 0 if an error occurred. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().receive(this->get_implementation(), + buffers, in_flags, out_flags, ec); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().receive_with_flags(this->get_implementation(), + buffers, in_flags, out_flags, ec); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the sequenced + * packet socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param out_flags Once the asynchronous operation completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. The caller must guarantee that the referenced + * variable remains valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), out_flags, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags& out_flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive( + this->get_implementation(), buffers, 0, out_flags, + ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive_with_flags( + this->get_implementation(), buffers, 0, out_flags, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the sequenced + * data socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param in_flags Flags specifying how the receive call is to be made. + * + * @param out_flags Once the asynchronous operation completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. The caller must guarantee that the referenced + * variable remains valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive( + * asio::buffer(data, size), + * 0, out_flags, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive( + this->get_implementation(), buffers, in_flags, out_flags, + ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive_with_flags( + this->get_implementation(), buffers, in_flags, out_flags, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SEQ_PACKET_SOCKET_HPP diff --git a/subprojects/asio/include/asio/basic_serial_port.hpp b/subprojects/asio/include/asio/basic_serial_port.hpp new file mode 100644 index 000000000..32262f842 --- /dev/null +++ b/subprojects/asio/include/asio/basic_serial_port.hpp @@ -0,0 +1,688 @@ +// +// basic_serial_port.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SERIAL_PORT_HPP +#define ASIO_BASIC_SERIAL_PORT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides serial port functionality. +/** + * The basic_serial_port class template provides functionality that is common + * to all serial ports. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_serial_port + : public basic_io_object, + public serial_port_base +{ +public: + /// The native representation of a serial port. + typedef typename SerialPortService::native_handle_type native_handle_type; + + /// A basic_serial_port is always the lowest layer. + typedef basic_serial_port lowest_layer_type; + + /// Construct a basic_serial_port without opening it. + /** + * This constructor creates a serial port without opening it. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + */ + explicit basic_serial_port(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param device The platform-specific device name for this serial + * port. + */ + explicit basic_serial_port(asio::io_context& io_context, + const char* device) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param device The platform-specific device name for this serial + * port. + */ + explicit basic_serial_port(asio::io_context& io_context, + const std::string& device) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct a basic_serial_port on an existing native serial port. + /** + * This constructor creates a serial port object to hold an existing native + * serial port. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + basic_serial_port(asio::io_context& io_context, + const native_handle_type& native_serial_port) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_serial_port from another. + /** + * This constructor moves a serial port from one object to another. + * + * @param other The other basic_serial_port object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_serial_port(io_context&) constructor. + */ + basic_serial_port(basic_serial_port&& other) + : basic_io_object( + ASIO_MOVE_CAST(basic_serial_port)(other)) + { + } + + /// Move-assign a basic_serial_port from another. + /** + * This assignment operator moves a serial port from one object to another. + * + * @param other The other basic_serial_port object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_serial_port(io_context&) constructor. + */ + basic_serial_port& operator=(basic_serial_port&& other) + { + basic_io_object::operator=( + ASIO_MOVE_CAST(basic_serial_port)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_serial_port cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_serial_port cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port for the specified device name. + * + * @param device The platform-specific device name. + * + * @throws asio::system_error Thrown on failure. + */ + void open(const std::string& device) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port using the given platform-specific + * device name. + * + * @param device The platform-specific device name. + * + * @param ec Set the indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID open(const std::string& device, + asio::error_code& ec) + { + this->get_service().open(this->get_implementation(), device, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& native_serial_port) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& native_serial_port, + asio::error_code& ec) + { + this->get_service().assign(this->get_implementation(), + native_serial_port, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the serial port is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native serial port representation. + /** + * This function may be used to obtain the underlying representation of the + * serial port. This is intended to allow access to native serial port + * functionality that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void send_break() + { + asio::error_code ec; + this->get_service().send_break(this->get_implementation(), ec); + asio::detail::throw_error(ec, "send_break"); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID send_break(asio::error_code& ec) + { + this->get_service().send_break(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void set_option(const SettableSerialPortOption& option) + { + asio::error_code ec; + this->get_service().set_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + ASIO_SYNC_OP_VOID set_option(const SettableSerialPortOption& option, + asio::error_code& ec) + { + this->get_service().set_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void get_option(GettableSerialPortOption& option) + { + asio::error_code ec; + this->get_service().get_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + ASIO_SYNC_OP_VOID get_option(GettableSerialPortOption& option, + asio::error_code& ec) + { + this->get_service().get_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().write_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the serial port. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the serial port. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + return this->get_service().async_write_some(this->get_implementation(), + buffers, ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().read_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the serial port. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + return this->get_service().async_read_some(this->get_implementation(), + buffers, ASIO_MOVE_CAST(ReadHandler)(handler)); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_BASIC_SERIAL_PORT_HPP diff --git a/subprojects/asio/include/asio/basic_signal_set.hpp b/subprojects/asio/include/asio/basic_signal_set.hpp new file mode 100644 index 000000000..cf34643f8 --- /dev/null +++ b/subprojects/asio/include/asio/basic_signal_set.hpp @@ -0,0 +1,391 @@ +// +// basic_signal_set.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SIGNAL_SET_HPP +#define ASIO_BASIC_SIGNAL_SET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/signal_set_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides signal functionality. +/** + * The basic_signal_set class template provides the ability to perform an + * asynchronous wait for one or more signals to occur. + * + * Most applications will use the asio::signal_set typedef. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * Performing an asynchronous wait: + * @code + * void handler( + * const asio::error_code& error, + * int signal_number) + * { + * if (!error) + * { + * // A signal occurred. + * } + * } + * + * ... + * + * // Construct a signal set registered for process termination. + * asio::signal_set signals(io_context, SIGINT, SIGTERM); + * + * // Start an asynchronous wait for one of the signals to occur. + * signals.async_wait(handler); + * @endcode + * + * @par Queueing of signal notifications + * + * If a signal is registered with a signal_set, and the signal occurs when + * there are no waiting handlers, then the signal notification is queued. The + * next async_wait operation on that signal_set will dequeue the notification. + * If multiple notifications are queued, subsequent async_wait operations + * dequeue them one at a time. Signal notifications are dequeued in order of + * ascending signal number. + * + * If a signal number is removed from a signal_set (using the @c remove or @c + * erase member functions) then any queued notifications for that signal are + * discarded. + * + * @par Multiple registration of signals + * + * The same signal number may be registered with different signal_set objects. + * When the signal occurs, one handler is called for each signal_set object. + * + * Note that multiple registration only works for signals that are registered + * using Asio. The application must not also register a signal handler using + * functions such as @c signal() or @c sigaction(). + * + * @par Signal masking on POSIX platforms + * + * POSIX allows signals to be blocked using functions such as @c sigprocmask() + * and @c pthread_sigmask(). For signals to be delivered, programs must ensure + * that any signals registered using signal_set objects are unblocked in at + * least one thread. + */ +template +class basic_signal_set + : public basic_io_object +{ +public: + /// Construct a signal set without adding any signals. + /** + * This constructor creates a signal set without registering for any signals. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + */ + explicit basic_signal_set(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct a signal set and add one signal. + /** + * This constructor creates a signal set and registers for one signal. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + * + * @param signal_number_1 The signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(io_context); + * signals.add(signal_number_1); @endcode + */ + basic_signal_set(asio::io_context& io_context, int signal_number_1) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add two signals. + /** + * This constructor creates a signal set and registers for two signals. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(io_context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); @endcode + */ + basic_signal_set(asio::io_context& io_context, int signal_number_1, + int signal_number_2) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + this->get_service().add(this->get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add three signals. + /** + * This constructor creates a signal set and registers for three signals. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @param signal_number_3 The third signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(io_context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); + * signals.add(signal_number_3); @endcode + */ + basic_signal_set(asio::io_context& io_context, int signal_number_1, + int signal_number_2, int signal_number_3) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + this->get_service().add(this->get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + this->get_service().add(this->get_implementation(), signal_number_3, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Add a signal to a signal_set. + /** + * This function adds the specified signal to the set. It has no effect if the + * signal is already in the set. + * + * @param signal_number The signal to be added to the set. + * + * @throws asio::system_error Thrown on failure. + */ + void add(int signal_number) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Add a signal to a signal_set. + /** + * This function adds the specified signal to the set. It has no effect if the + * signal is already in the set. + * + * @param signal_number The signal to be added to the set. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID add(int signal_number, asio::error_code& ec) + { + this->get_service().add(this->get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove a signal from a signal_set. + /** + * This function removes the specified signal from the set. It has no effect + * if the signal is not in the set. + * + * @param signal_number The signal to be removed from the set. + * + * @throws asio::system_error Thrown on failure. + * + * @note Removes any notifications that have been queued for the specified + * signal number. + */ + void remove(int signal_number) + { + asio::error_code ec; + this->get_service().remove(this->get_implementation(), signal_number, ec); + asio::detail::throw_error(ec, "remove"); + } + + /// Remove a signal from a signal_set. + /** + * This function removes the specified signal from the set. It has no effect + * if the signal is not in the set. + * + * @param signal_number The signal to be removed from the set. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Removes any notifications that have been queued for the specified + * signal number. + */ + ASIO_SYNC_OP_VOID remove(int signal_number, + asio::error_code& ec) + { + this->get_service().remove(this->get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove all signals from a signal_set. + /** + * This function removes all signals from the set. It has no effect if the set + * is already empty. + * + * @throws asio::system_error Thrown on failure. + * + * @note Removes all queued notifications. + */ + void clear() + { + asio::error_code ec; + this->get_service().clear(this->get_implementation(), ec); + asio::detail::throw_error(ec, "clear"); + } + + /// Remove all signals from a signal_set. + /** + * This function removes all signals from the set. It has no effect if the set + * is already empty. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Removes all queued notifications. + */ + ASIO_SYNC_OP_VOID clear(asio::error_code& ec) + { + this->get_service().clear(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Cancel all operations associated with the signal set. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the signal set. The handler for each cancelled + * operation will be invoked with the asio::error::operation_aborted + * error code. + * + * Cancellation does not alter the set of registered signals. + * + * @throws asio::system_error Thrown on failure. + * + * @note If a registered signal occurred before cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all operations associated with the signal set. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the signal set. The handler for each cancelled + * operation will be invoked with the asio::error::operation_aborted + * error code. + * + * Cancellation does not alter the set of registered signals. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note If a registered signal occurred before cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous operation to wait for a signal to be delivered. + /** + * This function may be used to initiate an asynchronous wait against the + * signal set. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li One of the registered signals in the signal set occurs; or + * + * @li The signal set was cancelled, in which case the handler is passed the + * error code asio::error::operation_aborted. + * + * @param handler The handler to be called when the signal occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * int signal_number // Indicates which signal occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(SignalHandler, + void (asio::error_code, int)) + async_wait(ASIO_MOVE_ARG(SignalHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a SignalHandler. + ASIO_SIGNAL_HANDLER_CHECK(SignalHandler, handler) type_check; + + return this->get_service().async_wait(this->get_implementation(), + ASIO_MOVE_CAST(SignalHandler)(handler)); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_BASIC_SIGNAL_SET_HPP diff --git a/subprojects/asio/include/asio/basic_socket.hpp b/subprojects/asio/include/asio/basic_socket.hpp new file mode 100644 index 000000000..3dfacb424 --- /dev/null +++ b/subprojects/asio/include/asio/basic_socket.hpp @@ -0,0 +1,1757 @@ +// +// basic_socket.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_HPP +#define ASIO_BASIC_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/post.hpp" +#include "asio/socket_base.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_ssocket_service.hpp" +# define ASIO_SVC_T detail::winrt_ssocket_service +# elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +# define ASIO_SVC_T detail::win_iocp_socket_service +# else +# include "asio/detail/reactive_socket_service.hpp" +# define ASIO_SVC_T detail::reactive_socket_service +# endif +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides socket functionality. +/** + * The basic_socket class template provides functionality that is common to both + * stream-oriented and datagram-oriented sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_socket + : ASIO_SVC_ACCESS basic_io_object, + public socket_base +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename ASIO_SVC_T::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +#if !defined(ASIO_NO_EXTENSIONS) + /// A basic_socket is always the lowest layer. + typedef basic_socket lowest_layer_type; +#endif // !defined(ASIO_NO_EXTENSIONS) + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param io_context The io_context object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_socket(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param io_context The io_context object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(asio::io_context& io_context, + const protocol_type& protocol) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param io_context The io_context object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(asio::io_context& io_context, + const endpoint_type& endpoint) + : basic_io_object(io_context) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + this->get_service().open(this->get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + this->get_service().bind(this->get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param io_context The io_context object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(asio::io_context& io_context, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + protocol, native_socket, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket from another. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(io_context&) constructor. + */ + basic_socket(basic_socket&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign a basic_socket from another. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(io_context&) constructor. + */ + basic_socket& operator=(basic_socket&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } + + // All sockets have access to each other's implementations. + template + friend class basic_socket; + + /// Move-construct a basic_socket from a socket of another protocol type. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(io_context&) constructor. + */ + template + basic_socket(basic_socket&& other, + typename enable_if::value>::type* = 0) + : basic_io_object( + other.get_service(), other.get_implementation()) + { + } + + /// Move-assign a basic_socket from a socket of another protocol type. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(io_context&) constructor. + */ + template + typename enable_if::value, + basic_socket>::type& operator=( + basic_socket&& other) + { + basic_socket tmp(std::move(other)); + basic_io_object::operator=(std::move(tmp)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_ENABLE_OLD_SERVICES) + // These functions are provided by basic_io_object<>. +#else // defined(ASIO_ENABLE_OLD_SERVICES) +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#if !defined(ASIO_NO_EXTENSIONS) + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } +#endif // !defined(ASIO_NO_EXTENSIONS) + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * socket.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * asio::error_code ec; + * socket.open(asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID open(const protocol_type& protocol, + asio::error_code& ec) + { + this->get_service().open(this->get_implementation(), protocol, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, + const native_handle_type& native_socket) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + protocol, native_socket, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, + const native_handle_type& native_socket, asio::error_code& ec) + { + this->get_service().assign(this->get_implementation(), + protocol, native_socket, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::error_code ec; + * socket.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @throws asio::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + asio::error_code ec; + native_handle_type s = this->get_service().release( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(asio::error_code& ec) + { + return this->get_service().release(this->get_implementation(), ec); + } + + /// Get the native socket representation. + /** + * This function may be used to obtain the underlying representation of the + * socket. This is intended to allow access to native socket functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls to cancel() will always fail with + * asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls to cancel() will always fail with + * asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + * + * @throws asio::system_error Thrown on failure. + */ + bool at_mark() const + { + asio::error_code ec; + bool b = this->get_service().at_mark(this->get_implementation(), ec); + asio::detail::throw_error(ec, "at_mark"); + return b; + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + */ + bool at_mark(asio::error_code& ec) const + { + return this->get_service().at_mark(this->get_implementation(), ec); + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t available() const + { + asio::error_code ec; + std::size_t s = this->get_service().available( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "available"); + return s; + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + */ + std::size_t available(asio::error_code& ec) const + { + return this->get_service().available(this->get_implementation(), ec); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * socket.open(asio::ip::tcp::v4()); + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + asio::error_code ec; + this->get_service().bind(this->get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * socket.open(asio::ip::tcp::v4()); + * asio::error_code ec; + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, + asio::error_code& ec) + { + this->get_service().bind(this->get_implementation(), endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.connect(endpoint); + * @endcode + */ + void connect(const endpoint_type& peer_endpoint) + { + asio::error_code ec; + if (!is_open()) + { + this->get_service().open(this->get_implementation(), + peer_endpoint.protocol(), ec); + asio::detail::throw_error(ec, "connect"); + } + this->get_service().connect(this->get_implementation(), peer_endpoint, ec); + asio::detail::throw_error(ec, "connect"); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * asio::error_code ec; + * socket.connect(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID connect(const endpoint_type& peer_endpoint, + asio::error_code& ec) + { + if (!is_open()) + { + this->get_service().open(this->get_implementation(), + peer_endpoint.protocol(), ec); + if (ec) + { + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + this->get_service().connect(this->get_implementation(), peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a socket to the specified + * remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void connect_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void (asio::error_code)) + async_connect(const endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(ConnectHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ConnectHandler. + ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check; + + if (!is_open()) + { + asio::error_code ec; + const protocol_type protocol = peer_endpoint.protocol(); + this->get_service().open(this->get_implementation(), protocol, ec); + if (ec) + { + async_completion init(handler); + + asio::post(this->get_executor(), + asio::detail::bind_handler( + ASIO_MOVE_CAST(ASIO_HANDLER_TYPE( + ConnectHandler, void (asio::error_code)))( + init.completion_handler), ec)); + + return init.result.get(); + } + } + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_connect(this->get_implementation(), + peer_endpoint, ASIO_MOVE_CAST(ConnectHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_connect( + this->get_implementation(), peer_endpoint, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + */ + template + void set_option(const SettableSocketOption& option) + { + asio::error_code ec; + this->get_service().set_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::no_delay option(true); + * asio::error_code ec; + * socket.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, + asio::error_code& ec) + { + this->get_service().set_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::socket::keep_alive option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + */ + template + void get_option(GettableSocketOption& option) const + { + asio::error_code ec; + this->get_service().get_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::socket::keep_alive option; + * asio::error_code ec; + * socket.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.value(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + asio::error_code& ec) const + { + this->get_service().get_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + this->get_service().io_control(this->get_implementation(), command, ec); + asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * asio::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + asio::error_code& ec) + { + this->get_service().io_control(this->get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + /** + * @returns @c true if the socket's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + bool non_blocking() const + { + return this->get_service().non_blocking(this->get_implementation()); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @throws asio::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + void non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().non_blocking(this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + ASIO_SYNC_OP_VOID non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().non_blocking(this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native socket. This mode has no effect on the behaviour of the socket + * object's synchronous operations. + * + * @returns @c true if the underlying socket is in non-blocking mode and + * direct system calls may fail with asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the socket object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native socket. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(asio::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = asio::error_code(n < 0 ? errno : 0, + * asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == asio::error::would_block + * || ec == asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + bool native_non_blocking() const + { + return this->get_service().native_non_blocking(this->get_implementation()); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @throws asio::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(asio::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = asio::error_code(n < 0 ? errno : 0, + * asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == asio::error::would_block + * || ec == asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + void native_non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(asio::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = asio::error_code(n < 0 ? errno : 0, + * asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == asio::error::would_block + * || ec == asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @returns An object that represents the local endpoint of the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + asio::error_code ec; + endpoint_type ep = this->get_service().local_endpoint( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "local_endpoint"); + return ep; + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(asio::error_code& ec) const + { + return this->get_service().local_endpoint(this->get_implementation(), ec); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @returns An object that represents the remote endpoint of the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); + * @endcode + */ + endpoint_type remote_endpoint() const + { + asio::error_code ec; + endpoint_type ep = this->get_service().remote_endpoint( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "remote_endpoint"); + return ep; + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the remote endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type remote_endpoint(asio::error_code& ec) const + { + return this->get_service().remote_endpoint(this->get_implementation(), ec); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * socket.shutdown(asio::ip::tcp::socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + asio::error_code ec; + this->get_service().shutdown(this->get_implementation(), what, ec); + asio::detail::throw_error(ec, "shutdown"); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::error_code ec; + * socket.shutdown(asio::ip::tcp::socket::shutdown_send, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID shutdown(shutdown_type what, + asio::error_code& ec) + { + this->get_service().shutdown(this->get_implementation(), what, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * socket.wait(asio::ip::tcp::socket::wait_read); + * @endcode + */ + void wait(wait_type w) + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), w, ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::error_code ec; + * socket.wait(asio::ip::tcp::socket::wait_read, ec); + * @endcode + */ + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void wait_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::socket socket(io_context); + * ... + * socket.async_wait(asio::ip::tcp::socket::wait_read, wait_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(wait_type w, ASIO_MOVE_ARG(WaitHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_wait(this->get_implementation(), + w, ASIO_MOVE_CAST(WaitHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_wait(this->get_implementation(), + w, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + +protected: + /// Protected destructor to prevent deletion through this type. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_socket() + { + } + +private: + // Disallow copying and assignment. + basic_socket(const basic_socket&) ASIO_DELETED; + basic_socket& operator=(const basic_socket&) ASIO_DELETED; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# undef ASIO_SVC_T +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_BASIC_SOCKET_HPP diff --git a/subprojects/asio/include/asio/basic_socket_acceptor.hpp b/subprojects/asio/include/asio/basic_socket_acceptor.hpp new file mode 100644 index 000000000..ed201bbca --- /dev/null +++ b/subprojects/asio/include/asio/basic_socket_acceptor.hpp @@ -0,0 +1,1986 @@ +// +// basic_socket_acceptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_ACCEPTOR_HPP +#define ASIO_BASIC_SOCKET_ACCEPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/socket_base.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/socket_acceptor_service.hpp" +#else // defined(ASIO_ENABLE_OLD_SERVICES) +# if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +# define ASIO_SVC_T detail::null_socket_service +# elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +# define ASIO_SVC_T detail::win_iocp_socket_service +# else +# include "asio/detail/reactive_socket_service.hpp" +# define ASIO_SVC_T detail::reactive_socket_service +# endif +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides the ability to accept new connections. +/** + * The basic_socket_acceptor class template is used for accepting new socket + * connections. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * Opening a socket acceptor with the SO_REUSEADDR option enabled: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port); + * acceptor.open(endpoint.protocol()); + * acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ +template )> +class basic_socket_acceptor + : ASIO_SVC_ACCESS basic_io_object, + public socket_base +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The native representation of an acceptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename ASIO_SVC_T::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param io_context The io_context object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + */ + explicit basic_socket_acceptor(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param io_context The io_context object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket_acceptor(asio::io_context& io_context, + const protocol_type& protocol) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param io_context The io_context object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws asio::system_error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor acceptor(io_context); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(listen_backlog); + * @endcode + */ + basic_socket_acceptor(asio::io_context& io_context, + const endpoint_type& endpoint, bool reuse_addr = true) + : basic_io_object(io_context) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + this->get_service().open(this->get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + if (reuse_addr) + { + this->get_service().set_option(this->get_implementation(), + socket_base::reuse_address(true), ec); + asio::detail::throw_error(ec, "set_option"); + } + this->get_service().bind(this->get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + this->get_service().listen(this->get_implementation(), + socket_base::max_listen_connections, ec); + asio::detail::throw_error(ec, "listen"); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param io_context The io_context object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket_acceptor(asio::io_context& io_context, + const protocol_type& protocol, const native_handle_type& native_acceptor) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + protocol, native_acceptor, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket_acceptor from another. + /** + * This constructor moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(io_context&) constructor. + */ + basic_socket_acceptor(basic_socket_acceptor&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign a basic_socket_acceptor from another. + /** + * This assignment operator moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(io_context&) constructor. + */ + basic_socket_acceptor& operator=(basic_socket_acceptor&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } + + // All socket acceptors have access to each other's implementations. + template + friend class basic_socket_acceptor; + + /// Move-construct a basic_socket_acceptor from an acceptor of another + /// protocol type. + /** + * This constructor moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(io_context&) constructor. + */ + template + basic_socket_acceptor( + basic_socket_acceptor&& other, + typename enable_if::value>::type* = 0) + : basic_io_object( + other.get_service(), other.get_implementation()) + { + } + + /// Move-assign a basic_socket_acceptor from an acceptor of another protocol + /// type. + /** + * This assignment operator moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(io_context&) constructor. + */ + template + typename enable_if::value, + basic_socket_acceptor>::type& operator=( + basic_socket_acceptor&& other) + { + basic_socket_acceptor tmp(std::move(other)); + basic_io_object::operator=(std::move(tmp)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the acceptor. + /** + * This function destroys the acceptor, cancelling any outstanding + * asynchronous operations associated with the acceptor as if by calling + * @c cancel. + */ + ~basic_socket_acceptor() + { + } + +#if defined(ASIO_ENABLE_OLD_SERVICES) + // These functions are provided by basic_io_object<>. +#else // defined(ASIO_ENABLE_OLD_SERVICES) +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * acceptor.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * asio::error_code ec; + * acceptor.open(asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID open(const protocol_type& protocol, + asio::error_code& ec) + { + this->get_service().open(this->get_implementation(), protocol, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, + const native_handle_type& native_acceptor) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + protocol, native_acceptor, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, + const native_handle_type& native_acceptor, asio::error_code& ec) + { + this->get_service().assign(this->get_implementation(), + protocol, native_acceptor, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the acceptor is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), 12345); + * acceptor.open(endpoint.protocol()); + * acceptor.bind(endpoint); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + asio::error_code ec; + this->get_service().bind(this->get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), 12345); + * acceptor.open(endpoint.protocol()); + * asio::error_code ec; + * acceptor.bind(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, + asio::error_code& ec) + { + this->get_service().bind(this->get_implementation(), endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @throws asio::system_error Thrown on failure. + */ + void listen(int backlog = socket_base::max_listen_connections) + { + asio::error_code ec; + this->get_service().listen(this->get_implementation(), backlog, ec); + asio::detail::throw_error(ec, "listen"); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::error_code ec; + * acceptor.listen(asio::socket_base::max_listen_connections, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID listen(int backlog, asio::error_code& ec) + { + this->get_service().listen(this->get_implementation(), backlog, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::error_code ec; + * acceptor.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @throws asio::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + asio::error_code ec; + native_handle_type s = this->get_service().release( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(asio::error_code& ec) + { + return this->get_service().release(this->get_implementation(), ec); + } + + /// Get the native acceptor representation. + /** + * This function may be used to obtain the underlying representation of the + * acceptor. This is intended to allow access to native acceptor functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + */ + template + void set_option(const SettableSocketOption& option) + { + asio::error_code ec; + this->get_service().set_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * asio::error_code ec; + * acceptor.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, + asio::error_code& ec) + { + this->get_service().set_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(GettableSocketOption& option) const + { + asio::error_code ec; + this->get_service().get_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * asio::error_code ec; + * acceptor.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + asio::error_code& ec) const + { + this->get_service().get_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the acceptor. + /** + * This function is used to execute an IO control command on the acceptor. + * + * @param command The IO control command to be performed on the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::acceptor::non_blocking_io command(true); + * socket.io_control(command); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + this->get_service().io_control(this->get_implementation(), command, ec); + asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the acceptor. + /** + * This function is used to execute an IO control command on the acceptor. + * + * @param command The IO control command to be performed on the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::acceptor::non_blocking_io command(true); + * asio::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + asio::error_code& ec) + { + this->get_service().io_control(this->get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the acceptor. + /** + * @returns @c true if the acceptor's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + bool non_blocking() const + { + return this->get_service().non_blocking(this->get_implementation()); + } + + /// Sets the non-blocking mode of the acceptor. + /** + * @param mode If @c true, the acceptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @throws asio::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + void non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().non_blocking(this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the acceptor. + /** + * @param mode If @c true, the acceptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + ASIO_SYNC_OP_VOID non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().non_blocking(this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native acceptor. This mode has no effect on the behaviour of the acceptor + * object's synchronous operations. + * + * @returns @c true if the underlying acceptor is in non-blocking mode and + * direct system calls may fail with asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the acceptor object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native acceptor. + */ + bool native_non_blocking() const + { + return this->get_service().native_non_blocking(this->get_implementation()); + } + + /// Sets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native acceptor. It has no effect on the behaviour of the acceptor object's + * synchronous operations. + * + * @param mode If @c true, the underlying acceptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @throws asio::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + void native_non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native acceptor. It has no effect on the behaviour of the acceptor object's + * synchronous operations. + * + * @param mode If @c true, the underlying acceptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @returns An object that represents the local endpoint of the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + asio::error_code ec; + endpoint_type ep = this->get_service().local_endpoint( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "local_endpoint"); + return ep; + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the acceptor. + * Returns a default-constructed endpoint object if an error occurred and the + * error handler did not throw an exception. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(asio::error_code& ec) const + { + return this->get_service().local_endpoint(this->get_implementation(), ec); + } + + /// Wait for the acceptor to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for an acceptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @par Example + * Waiting for an acceptor to become readable. + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * acceptor.wait(asio::ip::tcp::acceptor::wait_read); + * @endcode + */ + void wait(wait_type w) + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), w, ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the acceptor to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for an acceptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for an acceptor to become readable. + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::error_code ec; + * acceptor.wait(asio::ip::tcp::acceptor::wait_read, ec); + * @endcode + */ + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the acceptor to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for an acceptor to + * enter a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void wait_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * acceptor.async_wait( + * asio::ip::tcp::acceptor::wait_read, + * wait_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(wait_type w, ASIO_MOVE_ARG(WaitHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_wait(this->get_implementation(), + w, ASIO_MOVE_CAST(WaitHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_wait(this->get_implementation(), + w, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + +#if !defined(ASIO_NO_EXTENSIONS) + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(io_context); + * acceptor.accept(socket); + * @endcode + */ +#if defined(ASIO_ENABLE_OLD_SERVICES) + template + void accept(basic_socket& peer, + typename enable_if::value>::type* = 0) +#else // defined(ASIO_ENABLE_OLD_SERVICES) + template + void accept(basic_socket& peer, + typename enable_if::value>::type* = 0) +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + { + asio::error_code ec; + this->get_service().accept(this->get_implementation(), + peer, static_cast(0), ec); + asio::detail::throw_error(ec, "accept"); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(io_context); + * asio::error_code ec; + * acceptor.accept(socket, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ +#if defined(ASIO_ENABLE_OLD_SERVICES) + template + ASIO_SYNC_OP_VOID accept( + basic_socket& peer, + asio::error_code& ec, + typename enable_if::value>::type* = 0) +#else // defined(ASIO_ENABLE_OLD_SERVICES) + template + ASIO_SYNC_OP_VOID accept( + basic_socket& peer, asio::error_code& ec, + typename enable_if::value>::type* = 0) +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + { + this->get_service().accept(this->get_implementation(), + peer, static_cast(0), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket. The function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(io_context); + * acceptor.async_accept(socket, accept_handler); + * @endcode + */ +#if defined(ASIO_ENABLE_OLD_SERVICES) + template + ASIO_INITFN_RESULT_TYPE(AcceptHandler, + void (asio::error_code)) + async_accept(basic_socket& peer, + ASIO_MOVE_ARG(AcceptHandler) handler, + typename enable_if::value>::type* = 0) +#else // defined(ASIO_ENABLE_OLD_SERVICES) + template + ASIO_INITFN_RESULT_TYPE(AcceptHandler, + void (asio::error_code)) + async_accept(basic_socket& peer, + ASIO_MOVE_ARG(AcceptHandler) handler, + typename enable_if::value>::type* = 0) +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a AcceptHandler. + ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_accept(this->get_implementation(), + peer, static_cast(0), + ASIO_MOVE_CAST(AcceptHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_accept(this->get_implementation(), + peer, static_cast(0), init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::endpoint endpoint; + * acceptor.accept(socket, endpoint); + * @endcode + */ +#if defined(ASIO_ENABLE_OLD_SERVICES) + template + void accept(basic_socket& peer, + endpoint_type& peer_endpoint) +#else // defined(ASIO_ENABLE_OLD_SERVICES) + void accept(basic_socket& peer, endpoint_type& peer_endpoint) +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + { + asio::error_code ec; + this->get_service().accept(this->get_implementation(), + peer, &peer_endpoint, ec); + asio::detail::throw_error(ec, "accept"); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::endpoint endpoint; + * asio::error_code ec; + * acceptor.accept(socket, endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ +#if defined(ASIO_ENABLE_OLD_SERVICES) + template + ASIO_SYNC_OP_VOID accept( + basic_socket& peer, + endpoint_type& peer_endpoint, asio::error_code& ec) +#else // defined(ASIO_ENABLE_OLD_SERVICES) + ASIO_SYNC_OP_VOID accept(basic_socket& peer, + endpoint_type& peer_endpoint, asio::error_code& ec) +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + { + this->get_service().accept( + this->get_implementation(), peer, &peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket, and additionally obtain the endpoint of the remote peer. The + * function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +#if defined(ASIO_ENABLE_OLD_SERVICES) + template + ASIO_INITFN_RESULT_TYPE(AcceptHandler, + void (asio::error_code)) + async_accept(basic_socket& peer, + endpoint_type& peer_endpoint, ASIO_MOVE_ARG(AcceptHandler) handler) +#else // defined(ASIO_ENABLE_OLD_SERVICES) + template + ASIO_INITFN_RESULT_TYPE(AcceptHandler, + void (asio::error_code)) + async_accept(basic_socket& peer, + endpoint_type& peer_endpoint, ASIO_MOVE_ARG(AcceptHandler) handler) +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a AcceptHandler. + ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_accept(this->get_implementation(), peer, + &peer_endpoint, ASIO_MOVE_CAST(AcceptHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_accept(this->get_implementation(), + peer, &peer_endpoint, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +#endif // !defined(ASIO_NO_EXTENSIONS) + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + typename Protocol::socket accept() + { + asio::error_code ec; + typename Protocol::socket peer( + this->get_service().accept( + this->get_implementation(), 0, 0, ec)); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept(ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket accept(asio::error_code& ec) + { + return this->get_service().accept(this->get_implementation(), 0, 0, ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * acceptor.async_accept(accept_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, typename Protocol::socket)) + async_accept(ASIO_MOVE_ARG(MoveAcceptHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a MoveAcceptHandler. + ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, + handler, typename Protocol::socket) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_accept( + this->get_implementation(), static_cast(0), + static_cast(0), + ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_accept( + this->get_implementation(), static_cast(0), + static_cast(0), init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param io_context The io_context object to be used for the newly accepted + * socket. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + typename Protocol::socket accept(asio::io_context& io_context) + { + asio::error_code ec; + typename Protocol::socket peer( + this->get_service().accept(this->get_implementation(), + &io_context, static_cast(0), ec)); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param io_context The io_context object to be used for the newly accepted + * socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept(io_context2, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket accept( + asio::io_context& io_context, asio::error_code& ec) + { + return this->get_service().accept(this->get_implementation(), + &io_context, static_cast(0), ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param io_context The io_context object to be used for the newly accepted + * socket. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * acceptor.async_accept(io_context2, accept_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, typename Protocol::socket)) + async_accept(asio::io_context& io_context, + ASIO_MOVE_ARG(MoveAcceptHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a MoveAcceptHandler. + ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, + handler, typename Protocol::socket) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_accept(this->get_implementation(), + &io_context, static_cast(0), + ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_accept(this->get_implementation(), + &io_context, static_cast(0), init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket(acceptor.accept(endpoint)); + * @endcode + */ + typename Protocol::socket accept(endpoint_type& peer_endpoint) + { + asio::error_code ec; + typename Protocol::socket peer( + this->get_service().accept(this->get_implementation(), + static_cast(0), &peer_endpoint, ec)); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket(acceptor.accept(endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket accept( + endpoint_type& peer_endpoint, asio::error_code& ec) + { + return this->get_service().accept(this->get_implementation(), + static_cast(0), &peer_endpoint, ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(endpoint, accept_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, typename Protocol::socket)) + async_accept(endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a MoveAcceptHandler. + ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, + handler, typename Protocol::socket) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_accept(this->get_implementation(), + static_cast(0), &peer_endpoint, + ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_accept(this->get_implementation(), + static_cast(0), &peer_endpoint, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param io_context The io_context object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(io_context2, endpoint)); + * @endcode + */ + typename Protocol::socket accept( + asio::io_context& io_context, endpoint_type& peer_endpoint) + { + asio::error_code ec; + typename Protocol::socket peer( + this->get_service().accept(this->get_implementation(), + &io_context, &peer_endpoint, ec)); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param io_context The io_context object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(io_context2, endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket accept(asio::io_context& io_context, + endpoint_type& peer_endpoint, asio::error_code& ec) + { + return this->get_service().accept(this->get_implementation(), + &io_context, &peer_endpoint, ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param io_context The io_context object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(io_context2, endpoint, accept_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, typename Protocol::socket)) + async_accept(asio::io_context& io_context, + endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a MoveAcceptHandler. + ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, + handler, typename Protocol::socket) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_accept( + this->get_implementation(), &io_context, &peer_endpoint, + ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_accept(this->get_implementation(), + &io_context, &peer_endpoint, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# undef ASIO_SVC_T +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP diff --git a/subprojects/asio/include/asio/basic_socket_iostream.hpp b/subprojects/asio/include/asio/basic_socket_iostream.hpp new file mode 100644 index 000000000..6681367fd --- /dev/null +++ b/subprojects/asio/include/asio/basic_socket_iostream.hpp @@ -0,0 +1,430 @@ +// +// basic_socket_iostream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP +#define ASIO_BASIC_SOCKET_IOSTREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include +#include +#include "asio/basic_socket_streambuf.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/stream_socket_service.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +# include "asio/detail/variadic_templates.hpp" + +// A macro that should expand to: +// template +// explicit basic_socket_iostream(T1 x1, ..., Tn xn) +// : std::basic_iostream( +// &this->detail::socket_iostream_base< +// Protocol ASIO_SVC_TARG, Clock, +// WaitTraits ASIO_SVC_TARG1>::streambuf_) +// { +// if (rdbuf()->connect(x1, ..., xn) == 0) +// this->setstate(std::ios_base::failbit); +// } +// This macro should only persist within this file. + +# define ASIO_PRIVATE_CTR_DEF(n) \ + template \ + explicit basic_socket_iostream(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ + : std::basic_iostream( \ + &this->detail::socket_iostream_base< \ + Protocol ASIO_SVC_TARG, Clock, \ + WaitTraits ASIO_SVC_TARG1>::streambuf_) \ + { \ + this->setf(std::ios_base::unitbuf); \ + if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \ + this->setstate(std::ios_base::failbit); \ + } \ + /**/ + +// A macro that should expand to: +// template +// void connect(T1 x1, ..., Tn xn) +// { +// if (rdbuf()->connect(x1, ..., xn) == 0) +// this->setstate(std::ios_base::failbit); +// } +// This macro should only persist within this file. + +# define ASIO_PRIVATE_CONNECT_DEF(n) \ + template \ + void connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ + { \ + if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \ + this->setstate(std::ios_base::failbit); \ + } \ + /**/ + +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// A separate base class is used to ensure that the streambuf is initialised +// prior to the basic_socket_iostream's basic_iostream base class. +template +class socket_iostream_base +{ +protected: + socket_iostream_base() + { + } + +#if defined(ASIO_HAS_MOVE) + socket_iostream_base(socket_iostream_base&& other) + : streambuf_(std::move(other.streambuf_)) + { + } + + socket_iostream_base(basic_stream_socket s) + : streambuf_(std::move(s)) + { + } + + socket_iostream_base& operator=(socket_iostream_base&& other) + { + streambuf_ = std::move(other.streambuf_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + basic_socket_streambuf streambuf_; +}; + +} // namespace detail + +#if !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) +#define ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL + +// Forward declaration with defaulted arguments. +template ), +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typename Clock = boost::posix_time::ptime, + typename WaitTraits = time_traits + ASIO_SVC_TPARAM1_DEF2(= deadline_timer_service)> +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typename Clock = chrono::steady_clock, + typename WaitTraits = wait_traits + ASIO_SVC_TPARAM1_DEF1(= steady_timer::service_type)> +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +class basic_socket_iostream; + +#endif // !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) + +/// Iostream interface for a socket. +#if defined(GENERATING_DOCUMENTATION) +template > +#else // defined(GENERATING_DOCUMENTATION) +template +#endif // defined(GENERATING_DOCUMENTATION) +class basic_socket_iostream + : private detail::socket_iostream_base, + public std::basic_iostream +{ +private: + // These typedefs are intended keep this class's implementation independent + // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono. +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef WaitTraits traits_helper; +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef detail::chrono_time_traits traits_helper; +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The clock type. + typedef Clock clock_type; + +#if defined(GENERATING_DOCUMENTATION) + /// (Deprecated: Use time_point.) The time type. + typedef typename WaitTraits::time_type time_type; + + /// The time type. + typedef typename WaitTraits::time_point time_point; + + /// (Deprecated: Use duration.) The duration type. + typedef typename WaitTraits::duration_type duration_type; + + /// The duration type. + typedef typename WaitTraits::duration duration; +#else +# if !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_type; + typedef typename traits_helper::duration_type duration_type; +# endif // !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_point; + typedef typename traits_helper::duration_type duration; +#endif + + /// Construct a basic_socket_iostream without establishing a connection. + basic_socket_iostream() + : std::basic_iostream( + &this->detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>::streambuf_) + { + this->setf(std::ios_base::unitbuf); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Construct a basic_socket_iostream from the supplied socket. + explicit basic_socket_iostream(basic_stream_socket s) + : detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>(std::move(s)), + std::basic_iostream( + &this->detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>::streambuf_) + { + this->setf(std::ios_base::unitbuf); + } + +#if defined(ASIO_HAS_STD_IOSTREAM_MOVE) \ + || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket_iostream from another. + basic_socket_iostream(basic_socket_iostream&& other) + : detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>(std::move(other)), + std::basic_iostream(std::move(other)) + { + this->set_rdbuf(&this->detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>::streambuf_); + } + + /// Move-assign a basic_socket_iostream from another. + basic_socket_iostream& operator=(basic_socket_iostream&& other) + { + std::basic_iostream::operator=(std::move(other)); + detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_STD_IOSTREAM_MOVE) + // || defined(GENERATING_DOCUMENTATION) +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This constructor automatically establishes a connection based on the + * supplied resolver query parameters. The arguments are used to construct + * a resolver query object. + */ + template + explicit basic_socket_iostream(T1 t1, ..., TN tn); +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + template + explicit basic_socket_iostream(T... x) + : std::basic_iostream( + &this->detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>::streambuf_) + { + this->setf(std::ios_base::unitbuf); + if (rdbuf()->connect(x...) == 0) + this->setstate(std::ios_base::failbit); + } +#else + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CTR_DEF) +#endif + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + */ + template + void connect(T1 t1, ..., TN tn); +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + template + void connect(T... x) + { + if (rdbuf()->connect(x...) == 0) + this->setstate(std::ios_base::failbit); + } +#else + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF) +#endif + + /// Close the connection. + void close() + { + if (rdbuf()->close() == 0) + this->setstate(std::ios_base::failbit); + } + + /// Return a pointer to the underlying streambuf. + basic_socket_streambuf* rdbuf() const + { + return const_cast*>( + &this->detail::socket_iostream_base< + Protocol ASIO_SVC_TARG, Clock, + WaitTraits ASIO_SVC_TARG1>::streambuf_); + } + + /// Get a reference to the underlying socket. + basic_socket& socket() + { + return rdbuf()->socket(); + } + + /// Get the last error associated with the stream. + /** + * @return An \c error_code corresponding to the last error from the stream. + * + * @par Example + * To print the error associated with a failure to establish a connection: + * @code tcp::iostream s("www.boost.org", "http"); + * if (!s) + * { + * std::cout << "Error: " << s.error().message() << std::endl; + * } @endcode + */ + const asio::error_code& error() const + { + return rdbuf()->error(); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the stream's expiry time as an absolute + /// time. + /** + * @return An absolute time value representing the stream's expiry time. + */ + time_point expires_at() const + { + return rdbuf()->expires_at(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the stream's expiry time as an absolute time. + /** + * @return An absolute time value representing the stream's expiry time. + */ + time_point expiry() const + { + return rdbuf()->expiry(); + } + + /// Set the stream's expiry time as an absolute time. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the stream. + */ + void expires_at(const time_point& expiry_time) + { + rdbuf()->expires_at(expiry_time); + } + + /// Set the stream's expiry time relative to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_after(const duration& expiry_time) + { + rdbuf()->expires_after(expiry_time); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the stream's expiry time relative to now. + /** + * @return A relative time value representing the stream's expiry time. + */ + duration expires_from_now() const + { + return rdbuf()->expires_from_now(); + } + + /// (Deprecated: Use expires_after().) Set the stream's expiry time relative + /// to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_from_now(const duration& expiry_time) + { + rdbuf()->expires_from_now(expiry_time); + } +#endif // !defined(ASIO_NO_DEPRECATED) + +private: + // Disallow copying and assignment. + basic_socket_iostream(const basic_socket_iostream&) ASIO_DELETED; + basic_socket_iostream& operator=( + const basic_socket_iostream&) ASIO_DELETED; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) +# undef ASIO_PRIVATE_CTR_DEF +# undef ASIO_PRIVATE_CONNECT_DEF +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP diff --git a/subprojects/asio/include/asio/basic_socket_streambuf.hpp b/subprojects/asio/include/asio/basic_socket_streambuf.hpp new file mode 100644 index 000000000..56a3637dc --- /dev/null +++ b/subprojects/asio/include/asio/basic_socket_streambuf.hpp @@ -0,0 +1,707 @@ +// +// basic_socket_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP +#define ASIO_BASIC_SOCKET_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include +#include +#include "asio/basic_socket.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/stream_socket_service.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +# if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/deadline_timer_service.hpp" +# else // defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/detail/deadline_timer_service.hpp" +# endif // defined(ASIO_ENABLE_OLD_SERVICES) +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +# include "asio/steady_timer.hpp" +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +# include "asio/detail/variadic_templates.hpp" + +// A macro that should expand to: +// template +// basic_socket_streambuf* connect(T1 x1, ..., Tn xn) +// { +// init_buffers(); +// typedef typename Protocol::resolver resolver_type; +// resolver_type resolver(socket().get_executor().context()); +// connect_to_endpoints( +// resolver.resolve(x1, ..., xn, ec_)); +// return !ec_ ? this : 0; +// } +// This macro should only persist within this file. + +# define ASIO_PRIVATE_CONNECT_DEF(n) \ + template \ + basic_socket_streambuf* connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ + { \ + init_buffers(); \ + typedef typename Protocol::resolver resolver_type; \ + resolver_type resolver(socket().get_executor().context()); \ + connect_to_endpoints( \ + resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \ + return !ec_ ? this : 0; \ + } \ + /**/ + +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# define ASIO_SVC_T1 detail::deadline_timer_service +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// A separate base class is used to ensure that the io_context member is +// initialised prior to the basic_socket_streambuf's basic_socket base class. +class socket_streambuf_io_context +{ +protected: + socket_streambuf_io_context(io_context* ctx) + : default_io_context_(ctx) + { + } + + shared_ptr default_io_context_; +}; + +// A separate base class is used to ensure that the dynamically allocated +// buffers are constructed prior to the basic_socket_streambuf's basic_socket +// base class. This makes moving the socket is the last potentially throwing +// step in the streambuf's move constructor, giving the constructor a strong +// exception safety guarantee. +class socket_streambuf_buffers +{ +protected: + socket_streambuf_buffers() + : get_buffer_(buffer_size), + put_buffer_(buffer_size) + { + } + + enum { buffer_size = 512 }; + std::vector get_buffer_; + std::vector put_buffer_; +}; + +} // namespace detail + +#if !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) +#define ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL + +// Forward declaration with defaulted arguments. +template ), +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typename Clock = boost::posix_time::ptime, + typename WaitTraits = time_traits + ASIO_SVC_TPARAM1_DEF2(= deadline_timer_service)> +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typename Clock = chrono::steady_clock, + typename WaitTraits = wait_traits + ASIO_SVC_TPARAM1_DEF1(= steady_timer::service_type)> +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +class basic_socket_streambuf; + +#endif // !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) + +/// Iostream streambuf for a socket. +#if defined(GENERATING_DOCUMENTATION) +template > +#else // defined(GENERATING_DOCUMENTATION) +template +#endif // defined(GENERATING_DOCUMENTATION) +class basic_socket_streambuf + : public std::streambuf, + private detail::socket_streambuf_io_context, + private detail::socket_streambuf_buffers, +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + private basic_socket +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + public basic_socket +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +{ +private: + // These typedefs are intended keep this class's implementation independent + // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono. +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef WaitTraits traits_helper; +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef detail::chrono_time_traits traits_helper; +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The clock type. + typedef Clock clock_type; + +#if defined(GENERATING_DOCUMENTATION) + /// (Deprecated: Use time_point.) The time type. + typedef typename WaitTraits::time_type time_type; + + /// The time type. + typedef typename WaitTraits::time_point time_point; + + /// (Deprecated: Use duration.) The duration type. + typedef typename WaitTraits::duration_type duration_type; + + /// The duration type. + typedef typename WaitTraits::duration duration; +#else +# if !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_type; + typedef typename traits_helper::duration_type duration_type; +# endif // !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_point; + typedef typename traits_helper::duration_type duration; +#endif + + /// Construct a basic_socket_streambuf without establishing a connection. + basic_socket_streambuf() + : detail::socket_streambuf_io_context(new io_context), + basic_socket(*default_io_context_), + expiry_time_(max_expiry_time()) + { + init_buffers(); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Construct a basic_socket_streambuf from the supplied socket. + explicit basic_socket_streambuf(basic_stream_socket s) + : detail::socket_streambuf_io_context(0), + basic_socket(std::move(s)), + expiry_time_(max_expiry_time()) + { + init_buffers(); + } + + /// Move-construct a basic_socket_streambuf from another. + basic_socket_streambuf(basic_socket_streambuf&& other) + : detail::socket_streambuf_io_context(other), + basic_socket(std::move(other.socket())), + ec_(other.ec_), + expiry_time_(other.expiry_time_) + { + get_buffer_.swap(other.get_buffer_); + put_buffer_.swap(other.put_buffer_); + setg(other.eback(), other.gptr(), other.egptr()); + setp(other.pptr(), other.epptr()); + other.ec_ = asio::error_code(); + other.expiry_time_ = max_expiry_time(); + other.init_buffers(); + } + + /// Move-assign a basic_socket_streambuf from another. + basic_socket_streambuf& operator=(basic_socket_streambuf&& other) + { + this->close(); + socket() = std::move(other.socket()); + detail::socket_streambuf_io_context::operator=(other); + ec_ = other.ec_; + expiry_time_ = other.expiry_time_; + get_buffer_.swap(other.get_buffer_); + put_buffer_.swap(other.put_buffer_); + setg(other.eback(), other.gptr(), other.egptr()); + setp(other.pptr(), other.epptr()); + other.ec_ = asio::error_code(); + other.expiry_time_ = max_expiry_time(); + other.put_buffer_.resize(buffer_size); + other.init_buffers(); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor flushes buffered data. + virtual ~basic_socket_streambuf() + { + if (pptr() != pbase()) + overflow(traits_type::eof()); + } + + /// Establish a connection. + /** + * This function establishes a connection to the specified endpoint. + * + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + basic_socket_streambuf* connect(const endpoint_type& endpoint) + { + init_buffers(); + ec_ = asio::error_code(); + this->connect_to_endpoints(&endpoint, &endpoint + 1); + return !ec_ ? this : 0; + } + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + * + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + template + basic_socket_streambuf* connect(T1 t1, ..., TN tn); +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + template + basic_socket_streambuf* connect(T... x) + { + init_buffers(); + typedef typename Protocol::resolver resolver_type; + resolver_type resolver(socket().get_executor().context()); + connect_to_endpoints(resolver.resolve(x..., ec_)); + return !ec_ ? this : 0; + } +#else + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF) +#endif + + /// Close the connection. + /** + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + basic_socket_streambuf* close() + { + sync(); + socket().close(ec_); + if (!ec_) + init_buffers(); + return !ec_ ? this : 0; + } + + /// Get a reference to the underlying socket. + basic_socket& socket() + { + return *this; + } + + /// Get the last error associated with the stream buffer. + /** + * @return An \c error_code corresponding to the last error from the stream + * buffer. + */ + const asio::error_code& error() const + { + return ec_; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use error().) Get the last error associated with the stream + /// buffer. + /** + * @return An \c error_code corresponding to the last error from the stream + * buffer. + */ + const asio::error_code& puberror() const + { + return error(); + } + + /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an + /// absolute time. + /** + * @return An absolute time value representing the stream buffer's expiry + * time. + */ + time_point expires_at() const + { + return expiry_time_; + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the stream buffer's expiry time as an absolute time. + /** + * @return An absolute time value representing the stream buffer's expiry + * time. + */ + time_point expiry() const + { + return expiry_time_; + } + + /// Set the stream buffer's expiry time as an absolute time. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the stream. + */ + void expires_at(const time_point& expiry_time) + { + expiry_time_ = expiry_time; + } + + /// Set the stream buffer's expiry time relative to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_after(const duration& expiry_time) + { + expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the stream buffer's expiry time relative + /// to now. + /** + * @return A relative time value representing the stream buffer's expiry time. + */ + duration expires_from_now() const + { + return traits_helper::subtract(expires_at(), traits_helper::now()); + } + + /// (Deprecated: Use expires_after().) Set the stream buffer's expiry time + /// relative to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_from_now(const duration& expiry_time) + { + expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); + } +#endif // !defined(ASIO_NO_DEPRECATED) + +protected: + int_type underflow() + { +#if defined(ASIO_WINDOWS_RUNTIME) + ec_ = asio::error::operation_not_supported; + return traits_type::eof(); +#else // defined(ASIO_WINDOWS_RUNTIME) + if (gptr() != egptr()) + return traits_type::eof(); + + for (;;) + { + // Check if we are past the expiry time. + if (traits_helper::less_than(expiry_time_, traits_helper::now())) + { + ec_ = asio::error::timed_out; + return traits_type::eof(); + } + + // Try to complete the operation without blocking. + if (!socket().native_non_blocking()) + socket().native_non_blocking(true, ec_); + detail::buffer_sequence_adapter + bufs(asio::buffer(get_buffer_) + putback_max); + detail::signed_size_type bytes = detail::socket_ops::recv( + socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); + + // Check if operation succeeded. + if (bytes > 0) + { + setg(&get_buffer_[0], &get_buffer_[0] + putback_max, + &get_buffer_[0] + putback_max + bytes); + return traits_type::to_int_type(*gptr()); + } + + // Check for EOF. + if (bytes == 0) + { + ec_ = asio::error::eof; + return traits_type::eof(); + } + + // Operation failed. + if (ec_ != asio::error::would_block + && ec_ != asio::error::try_again) + return traits_type::eof(); + + // Wait for socket to become ready. + if (detail::socket_ops::poll_read( + socket().native_handle(), 0, timeout(), ec_) < 0) + return traits_type::eof(); + } +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + int_type overflow(int_type c) + { +#if defined(ASIO_WINDOWS_RUNTIME) + ec_ = asio::error::operation_not_supported; + return traits_type::eof(); +#else // defined(ASIO_WINDOWS_RUNTIME) + char_type ch = traits_type::to_char_type(c); + + // Determine what needs to be sent. + const_buffer output_buffer; + if (put_buffer_.empty()) + { + if (traits_type::eq_int_type(c, traits_type::eof())) + return traits_type::not_eof(c); // Nothing to do. + output_buffer = asio::buffer(&ch, sizeof(char_type)); + } + else + { + output_buffer = asio::buffer(pbase(), + (pptr() - pbase()) * sizeof(char_type)); + } + + while (output_buffer.size() > 0) + { + // Check if we are past the expiry time. + if (traits_helper::less_than(expiry_time_, traits_helper::now())) + { + ec_ = asio::error::timed_out; + return traits_type::eof(); + } + + // Try to complete the operation without blocking. + if (!socket().native_non_blocking()) + socket().native_non_blocking(true, ec_); + detail::buffer_sequence_adapter< + const_buffer, const_buffer> bufs(output_buffer); + detail::signed_size_type bytes = detail::socket_ops::send( + socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); + + // Check if operation succeeded. + if (bytes > 0) + { + output_buffer += static_cast(bytes); + continue; + } + + // Operation failed. + if (ec_ != asio::error::would_block + && ec_ != asio::error::try_again) + return traits_type::eof(); + + // Wait for socket to become ready. + if (detail::socket_ops::poll_write( + socket().native_handle(), 0, timeout(), ec_) < 0) + return traits_type::eof(); + } + + if (!put_buffer_.empty()) + { + setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); + + // If the new character is eof then our work here is done. + if (traits_type::eq_int_type(c, traits_type::eof())) + return traits_type::not_eof(c); + + // Add the new character to the output buffer. + *pptr() = ch; + pbump(1); + } + + return c; +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + int sync() + { + return overflow(traits_type::eof()); + } + + std::streambuf* setbuf(char_type* s, std::streamsize n) + { + if (pptr() == pbase() && s == 0 && n == 0) + { + put_buffer_.clear(); + setp(0, 0); + sync(); + return this; + } + + return 0; + } + +private: + // Disallow copying and assignment. + basic_socket_streambuf(const basic_socket_streambuf&) ASIO_DELETED; + basic_socket_streambuf& operator=( + const basic_socket_streambuf&) ASIO_DELETED; + + void init_buffers() + { + setg(&get_buffer_[0], + &get_buffer_[0] + putback_max, + &get_buffer_[0] + putback_max); + + if (put_buffer_.empty()) + setp(0, 0); + else + setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); + } + + int timeout() const + { + int64_t msec = traits_helper::to_posix_duration( + traits_helper::subtract(expiry_time_, + traits_helper::now())).total_milliseconds(); + if (msec > (std::numeric_limits::max)()) + msec = (std::numeric_limits::max)(); + else if (msec < 0) + msec = 0; + return static_cast(msec); + } + + template + void connect_to_endpoints(const EndpointSequence& endpoints) + { + this->connect_to_endpoints(endpoints.begin(), endpoints.end()); + } + + template + void connect_to_endpoints(EndpointIterator begin, EndpointIterator end) + { +#if defined(ASIO_WINDOWS_RUNTIME) + ec_ = asio::error::operation_not_supported; +#else // defined(ASIO_WINDOWS_RUNTIME) + if (ec_) + return; + + ec_ = asio::error::not_found; + for (EndpointIterator i = begin; i != end; ++i) + { + // Check if we are past the expiry time. + if (traits_helper::less_than(expiry_time_, traits_helper::now())) + { + ec_ = asio::error::timed_out; + return; + } + + // Close and reopen the socket. + typename Protocol::endpoint ep(*i); + socket().close(ec_); + socket().open(ep.protocol(), ec_); + if (ec_) + continue; + + // Try to complete the operation without blocking. + if (!socket().native_non_blocking()) + socket().native_non_blocking(true, ec_); + detail::socket_ops::connect(socket().native_handle(), + ep.data(), ep.size(), ec_); + + // Check if operation succeeded. + if (!ec_) + return; + + // Operation failed. + if (ec_ != asio::error::in_progress + && ec_ != asio::error::would_block) + continue; + + // Wait for socket to become ready. + if (detail::socket_ops::poll_connect( + socket().native_handle(), timeout(), ec_) < 0) + continue; + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (detail::socket_ops::getsockopt(socket().native_handle(), 0, + SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_) + == detail::socket_error_retval) + return; + + // Check the result of the connect operation. + ec_ = asio::error_code(connect_error, + asio::error::get_system_category()); + if (!ec_) + return; + } +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + // Helper function to get the maximum expiry time. + static time_point max_expiry_time() + { +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + return boost::posix_time::pos_infin; +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + return (time_point::max)(); +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + } + + enum { putback_max = 8 }; + asio::error_code ec_; + time_point expiry_time_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# undef ASIO_SVC_T1 +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) +# undef ASIO_PRIVATE_CONNECT_DEF +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP diff --git a/subprojects/asio/include/asio/basic_stream_socket.hpp b/subprojects/asio/include/asio/basic_stream_socket.hpp new file mode 100644 index 000000000..eea886274 --- /dev/null +++ b/subprojects/asio/include/asio/basic_stream_socket.hpp @@ -0,0 +1,921 @@ +// +// basic_stream_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAM_SOCKET_HPP +#define ASIO_BASIC_STREAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/stream_socket_service.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides stream-oriented socket functionality. +/** + * The basic_stream_socket class template provides asynchronous and blocking + * stream-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template )> +class basic_stream_socket + : public basic_socket +{ +public: + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket< + Protocol ASIO_SVC_TARG>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without opening it. The socket + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param io_context The io_context object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_stream_socket(asio::io_context& io_context) + : basic_socket(io_context) + { + } + + /// Construct and open a basic_stream_socket. + /** + * This constructor creates and opens a stream socket. The socket needs to be + * connected or accepted before data can be sent or received on it. + * + * @param io_context The io_context object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(asio::io_context& io_context, + const protocol_type& protocol) + : basic_socket(io_context, protocol) + { + } + + /// Construct a basic_stream_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a stream socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_context The io_context object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the stream + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(asio::io_context& io_context, + const endpoint_type& endpoint) + : basic_socket(io_context, endpoint) + { + } + + /// Construct a basic_stream_socket on an existing native socket. + /** + * This constructor creates a stream socket object to hold an existing native + * socket. + * + * @param io_context The io_context object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(asio::io_context& io_context, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket( + io_context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_stream_socket from another. + /** + * This constructor moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(io_context&) constructor. + */ + basic_stream_socket(basic_stream_socket&& other) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_stream_socket from another. + /** + * This assignment operator moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(io_context&) constructor. + */ + basic_stream_socket& operator=(basic_stream_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_stream_socket from a socket of another protocol + /// type. + /** + * This constructor moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(io_context&) constructor. + */ + template + basic_stream_socket( + basic_stream_socket&& other, + typename enable_if::value>::type* = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_stream_socket from a socket of another protocol type. + /** + * This assignment operator moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(io_context&) constructor. + */ + template + typename enable_if::value, + basic_stream_socket>::type& operator=( + basic_stream_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_stream_socket() + { + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. Returns 0 if an error occurred. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->get_service().send( + this->get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send( + this->get_implementation(), buffers, 0, + ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send( + this->get_implementation(), buffers, 0, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send( + this->get_implementation(), buffers, flags, + ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send( + this->get_implementation(), buffers, flags, + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. Returns 0 if an error occurred. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->get_service().receive( + this->get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive(this->get_implementation(), + buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive(this->get_implementation(), + buffers, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive(this->get_implementation(), + buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive(this->get_implementation(), + buffers, flags, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().send( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().send(this->get_implementation(), buffers, 0, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the socket. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_send(this->get_implementation(), + buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_send(this->get_implementation(), + buffers, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().receive( + this->get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().receive( + this->get_implementation(), buffers, 0, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_receive(this->get_implementation(), + buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_receive(this->get_implementation(), + buffers, 0, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_STREAM_SOCKET_HPP diff --git a/subprojects/asio/include/asio/basic_streambuf.hpp b/subprojects/asio/include/asio/basic_streambuf.hpp new file mode 100644 index 000000000..14f85d22f --- /dev/null +++ b/subprojects/asio/include/asio/basic_streambuf.hpp @@ -0,0 +1,452 @@ +// +// basic_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAMBUF_HPP +#define ASIO_BASIC_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include +#include +#include +#include +#include +#include "asio/basic_streambuf_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Automatically resizable buffer class based on std::streambuf. +/** + * The @c basic_streambuf class is derived from @c std::streambuf to associate + * the streambuf's input and output sequences with one or more character + * arrays. These character arrays are internal to the @c basic_streambuf + * object, but direct access to the array elements is provided to permit them + * to be used efficiently with I/O operations. Characters written to the output + * sequence of a @c basic_streambuf object are appended to the input sequence + * of the same object. + * + * The @c basic_streambuf class's public interface is intended to permit the + * following implementation strategies: + * + * @li A single contiguous character array, which is reallocated as necessary + * to accommodate changes in the size of the character sequence. This is the + * implementation approach currently used in Asio. + * + * @li A sequence of one or more character arrays, where each array is of the + * same size. Additional character array objects are appended to the sequence + * to accommodate changes in the size of the character sequence. + * + * @li A sequence of one or more character arrays of varying sizes. Additional + * character array objects are appended to the sequence to accommodate changes + * in the size of the character sequence. + * + * The constructor for basic_streambuf accepts a @c size_t argument specifying + * the maximum of the sum of the sizes of the input sequence and output + * sequence. During the lifetime of the @c basic_streambuf object, the following + * invariant holds: + * @code size() <= max_size()@endcode + * Any member function that would, if successful, cause the invariant to be + * violated shall throw an exception of class @c std::length_error. + * + * The constructor for @c basic_streambuf takes an Allocator argument. A copy + * of this argument is used for any memory allocation performed, by the + * constructor and by all member functions, during the lifetime of each @c + * basic_streambuf object. + * + * @par Examples + * Writing directly from an streambuf to a socket: + * @code + * asio::streambuf b; + * std::ostream os(&b); + * os << "Hello, World!\n"; + * + * // try sending some data in input sequence + * size_t n = sock.send(b.data()); + * + * b.consume(n); // sent data is removed from input sequence + * @endcode + * + * Reading from a socket directly into a streambuf: + * @code + * asio::streambuf b; + * + * // reserve 512 bytes in output sequence + * asio::streambuf::mutable_buffers_type bufs = b.prepare(512); + * + * size_t n = sock.receive(bufs); + * + * // received data is "committed" from output sequence to input sequence + * b.commit(n); + * + * std::istream is(&b); + * std::string s; + * is >> s; + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +template > +#else +template +#endif +class basic_streambuf + : public std::streambuf, + private noncopyable +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The type used to represent the input sequence as a list of buffers. + typedef implementation_defined const_buffers_type; + + /// The type used to represent the output sequence as a list of buffers. + typedef implementation_defined mutable_buffers_type; +#else + typedef ASIO_CONST_BUFFER const_buffers_type; + typedef ASIO_MUTABLE_BUFFER mutable_buffers_type; +#endif + + /// Construct a basic_streambuf object. + /** + * Constructs a streambuf with the specified maximum size. The initial size + * of the streambuf's input sequence is 0. + */ + explicit basic_streambuf( + std::size_t maximum_size = (std::numeric_limits::max)(), + const Allocator& allocator = Allocator()) + : max_size_(maximum_size), + buffer_(allocator) + { + std::size_t pend = (std::min)(max_size_, buffer_delta); + buffer_.resize((std::max)(pend, 1)); + setg(&buffer_[0], &buffer_[0], &buffer_[0]); + setp(&buffer_[0], &buffer_[0] + pend); + } + + /// Get the size of the input sequence. + /** + * @returns The size of the input sequence. The value is equal to that + * calculated for @c s in the following code: + * @code + * size_t s = 0; + * const_buffers_type bufs = data(); + * const_buffers_type::const_iterator i = bufs.begin(); + * while (i != bufs.end()) + * { + * const_buffer buf(*i++); + * s += buf.size(); + * } + * @endcode + */ + std::size_t size() const ASIO_NOEXCEPT + { + return pptr() - gptr(); + } + + /// Get the maximum size of the basic_streambuf. + /** + * @returns The allowed maximum of the sum of the sizes of the input sequence + * and output sequence. + */ + std::size_t max_size() const ASIO_NOEXCEPT + { + return max_size_; + } + + /// Get the current capacity of the basic_streambuf. + /** + * @returns The current total capacity of the streambuf, i.e. for both the + * input sequence and output sequence. + */ + std::size_t capacity() const ASIO_NOEXCEPT + { + return buffer_.capacity(); + } + + /// Get a list of buffers that represents the input sequence. + /** + * @returns An object of type @c const_buffers_type that satisfies + * ConstBufferSequence requirements, representing all character arrays in the + * input sequence. + * + * @note The returned object is invalidated by any @c basic_streambuf member + * function that modifies the input sequence or output sequence. + */ + const_buffers_type data() const ASIO_NOEXCEPT + { + return asio::buffer(asio::const_buffer(gptr(), + (pptr() - gptr()) * sizeof(char_type))); + } + + /// Get a list of buffers that represents the output sequence, with the given + /// size. + /** + * Ensures that the output sequence can accommodate @c n characters, + * reallocating character array objects as necessary. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing character array objects + * at the start of the output sequence such that the sum of the buffer sizes + * is @c n. + * + * @throws std::length_error If size() + n > max_size(). + * + * @note The returned object is invalidated by any @c basic_streambuf member + * function that modifies the input sequence or output sequence. + */ + mutable_buffers_type prepare(std::size_t n) + { + reserve(n); + return asio::buffer(asio::mutable_buffer( + pptr(), n * sizeof(char_type))); + } + + /// Move characters from the output sequence to the input sequence. + /** + * Appends @c n characters from the start of the output sequence to the input + * sequence. The beginning of the output sequence is advanced by @c n + * characters. + * + * Requires a preceding call prepare(x) where x >= n, and + * no intervening operations that modify the input or output sequence. + * + * @note If @c n is greater than the size of the output sequence, the entire + * output sequence is moved to the input sequence and no error is issued. + */ + void commit(std::size_t n) + { + n = std::min(n, epptr() - pptr()); + pbump(static_cast(n)); + setg(eback(), gptr(), pptr()); + } + + /// Remove characters from the input sequence. + /** + * Removes @c n characters from the beginning of the input sequence. + * + * @note If @c n is greater than the size of the input sequence, the entire + * input sequence is consumed and no error is issued. + */ + void consume(std::size_t n) + { + if (egptr() < pptr()) + setg(&buffer_[0], gptr(), pptr()); + if (gptr() + n > pptr()) + n = pptr() - gptr(); + gbump(static_cast(n)); + } + +protected: + enum { buffer_delta = 128 }; + + /// Override std::streambuf behaviour. + /** + * Behaves according to the specification of @c std::streambuf::underflow(). + */ + int_type underflow() + { + if (gptr() < pptr()) + { + setg(&buffer_[0], gptr(), pptr()); + return traits_type::to_int_type(*gptr()); + } + else + { + return traits_type::eof(); + } + } + + /// Override std::streambuf behaviour. + /** + * Behaves according to the specification of @c std::streambuf::overflow(), + * with the specialisation that @c std::length_error is thrown if appending + * the character to the input sequence would require the condition + * size() > max_size() to be true. + */ + int_type overflow(int_type c) + { + if (!traits_type::eq_int_type(c, traits_type::eof())) + { + if (pptr() == epptr()) + { + std::size_t buffer_size = pptr() - gptr(); + if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta) + { + reserve(max_size_ - buffer_size); + } + else + { + reserve(buffer_delta); + } + } + + *pptr() = traits_type::to_char_type(c); + pbump(1); + return c; + } + + return traits_type::not_eof(c); + } + + void reserve(std::size_t n) + { + // Get current stream positions as offsets. + std::size_t gnext = gptr() - &buffer_[0]; + std::size_t pnext = pptr() - &buffer_[0]; + std::size_t pend = epptr() - &buffer_[0]; + + // Check if there is already enough space in the put area. + if (n <= pend - pnext) + { + return; + } + + // Shift existing contents of get area to start of buffer. + if (gnext > 0) + { + pnext -= gnext; + std::memmove(&buffer_[0], &buffer_[0] + gnext, pnext); + } + + // Ensure buffer is large enough to hold at least the specified size. + if (n > pend - pnext) + { + if (n <= max_size_ && pnext <= max_size_ - n) + { + pend = pnext + n; + buffer_.resize((std::max)(pend, 1)); + } + else + { + std::length_error ex("asio::streambuf too long"); + asio::detail::throw_exception(ex); + } + } + + // Update stream positions. + setg(&buffer_[0], &buffer_[0], &buffer_[0] + pnext); + setp(&buffer_[0] + pnext, &buffer_[0] + pend); + } + +private: + std::size_t max_size_; + std::vector buffer_; + + // Helper function to get the preferred size for reading data. + friend std::size_t read_size_helper( + basic_streambuf& sb, std::size_t max_size) + { + return std::min( + std::max(512, sb.buffer_.capacity() - sb.size()), + std::min(max_size, sb.max_size() - sb.size())); + } +}; + +/// Adapts basic_streambuf to the dynamic buffer sequence type requirements. +#if defined(GENERATING_DOCUMENTATION) +template > +#else +template +#endif +class basic_streambuf_ref +{ +public: + /// The type used to represent the input sequence as a list of buffers. + typedef typename basic_streambuf::const_buffers_type + const_buffers_type; + + /// The type used to represent the output sequence as a list of buffers. + typedef typename basic_streambuf::mutable_buffers_type + mutable_buffers_type; + + /// Construct a basic_streambuf_ref for the given basic_streambuf object. + explicit basic_streambuf_ref(basic_streambuf& sb) + : sb_(sb) + { + } + + /// Copy construct a basic_streambuf_ref. + basic_streambuf_ref(const basic_streambuf_ref& other) ASIO_NOEXCEPT + : sb_(other.sb_) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move construct a basic_streambuf_ref. + basic_streambuf_ref(basic_streambuf_ref&& other) ASIO_NOEXCEPT + : sb_(other.sb_) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get the size of the input sequence. + std::size_t size() const ASIO_NOEXCEPT + { + return sb_.size(); + } + + /// Get the maximum size of the dynamic buffer. + std::size_t max_size() const ASIO_NOEXCEPT + { + return sb_.max_size(); + } + + /// Get the current capacity of the dynamic buffer. + std::size_t capacity() const ASIO_NOEXCEPT + { + return sb_.capacity(); + } + + /// Get a list of buffers that represents the input sequence. + const_buffers_type data() const ASIO_NOEXCEPT + { + return sb_.data(); + } + + /// Get a list of buffers that represents the output sequence, with the given + /// size. + mutable_buffers_type prepare(std::size_t n) + { + return sb_.prepare(n); + } + + /// Move bytes from the output sequence to the input sequence. + void commit(std::size_t n) + { + return sb_.commit(n); + } + + /// Remove characters from the input sequence. + void consume(std::size_t n) + { + return sb_.consume(n); + } + +private: + basic_streambuf& sb_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_STREAMBUF_HPP diff --git a/subprojects/asio/include/asio/basic_streambuf_fwd.hpp b/subprojects/asio/include/asio/basic_streambuf_fwd.hpp new file mode 100644 index 000000000..ed54fe956 --- /dev/null +++ b/subprojects/asio/include/asio/basic_streambuf_fwd.hpp @@ -0,0 +1,36 @@ +// +// basic_streambuf_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAMBUF_FWD_HPP +#define ASIO_BASIC_STREAMBUF_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include + +namespace asio { + +template > +class basic_streambuf; + +template > +class basic_streambuf_ref; + +} // namespace asio + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_STREAMBUF_FWD_HPP diff --git a/subprojects/asio/include/asio/basic_waitable_timer.hpp b/subprojects/asio/include/asio/basic_waitable_timer.hpp new file mode 100644 index 000000000..22b85a698 --- /dev/null +++ b/subprojects/asio/include/asio/basic_waitable_timer.hpp @@ -0,0 +1,705 @@ +// +// basic_waitable_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_WAITABLE_TIMER_HPP +#define ASIO_BASIC_WAITABLE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/wait_traits.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/waitable_timer_service.hpp" +#else // defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/detail/chrono_time_traits.hpp" +# include "asio/detail/deadline_timer_service.hpp" +# define ASIO_SVC_T \ + detail::deadline_timer_service< \ + detail::chrono_time_traits > +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) +#define ASIO_BASIC_WAITABLE_TIMER_FWD_DECL + +// Forward declaration with defaulted arguments. +template + ASIO_SVC_TPARAM_DEF2(= waitable_timer_service)> +class basic_waitable_timer; + +#endif // !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) + +/// Provides waitable timer functionality. +/** + * The basic_waitable_timer class template provides the ability to perform a + * blocking or asynchronous wait for a timer to expire. + * + * A waitable timer is always in one of two states: "expired" or "not expired". + * If the wait() or async_wait() function is called on an expired timer, the + * wait operation will complete immediately. + * + * Most applications will use one of the asio::steady_timer, + * asio::system_timer or asio::high_resolution_timer typedefs. + * + * @note This waitable timer functionality is for use with the C++11 standard + * library's @c <chrono> facility, or with the Boost.Chrono library. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Examples + * Performing a blocking wait (C++11): + * @code + * // Construct a timer without setting an expiry time. + * asio::steady_timer timer(io_context); + * + * // Set an expiry time relative to now. + * timer.expires_after(std::chrono::seconds(5)); + * + * // Wait for the timer to expire. + * timer.wait(); + * @endcode + * + * @par + * Performing an asynchronous wait (C++11): + * @code + * void handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Timer expired. + * } + * } + * + * ... + * + * // Construct a timer with an absolute expiry time. + * asio::steady_timer timer(io_context, + * std::chrono::steady_clock::now() + std::chrono::seconds(60)); + * + * // Start an asynchronous wait. + * timer.async_wait(handler); + * @endcode + * + * @par Changing an active waitable timer's expiry time + * + * Changing the expiry time of a timer while there are pending asynchronous + * waits causes those wait operations to be cancelled. To ensure that the action + * associated with the timer is performed only once, use something like this: + * used: + * + * @code + * void on_some_event() + * { + * if (my_timer.expires_after(seconds(5)) > 0) + * { + * // We managed to cancel the timer. Start new asynchronous wait. + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const asio::error_code& e) + * { + * if (e != asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @li The asio::basic_waitable_timer::expires_after() function + * cancels any pending asynchronous waits, and returns the number of + * asynchronous waits that were cancelled. If it returns 0 then you were too + * late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the asio::error_code passed to + * it contains the value asio::error::operation_aborted. + */ +template +class basic_waitable_timer + : ASIO_SVC_ACCESS basic_io_object +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The clock type. + typedef Clock clock_type; + + /// The duration type of the clock. + typedef typename clock_type::duration duration; + + /// The time point type of the clock. + typedef typename clock_type::time_point time_point; + + /// The wait traits type. + typedef WaitTraits traits_type; + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_after() functions must be called to set an expiry + * time before the timer can be waited on. + * + * @param io_context The io_context object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + */ + explicit basic_waitable_timer(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_context The io_context object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + basic_waitable_timer(asio::io_context& io_context, + const time_point& expiry_time) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().expires_at(this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_context The io_context object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + basic_waitable_timer(asio::io_context& io_context, + const duration& expiry_time) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().expires_after( + this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_after"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_waitable_timer from another. + /** + * This constructor moves a timer from one object to another. + * + * @param other The other basic_waitable_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_waitable_timer(io_context&) constructor. + */ + basic_waitable_timer(basic_waitable_timer&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign a basic_waitable_timer from another. + /** + * This assignment operator moves a timer from one object to another. Cancels + * any outstanding asynchronous operations associated with the target object. + * + * @param other The other basic_waitable_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_waitable_timer(io_context&) constructor. + */ + basic_waitable_timer& operator=(basic_waitable_timer&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the timer. + /** + * This function destroys the timer, cancelling any outstanding asynchronous + * wait operations associated with the timer as if by calling @c cancel. + */ + ~basic_waitable_timer() + { + } + +#if defined(ASIO_ENABLE_OLD_SERVICES) + // These functions are provided by basic_io_object<>. +#else // defined(ASIO_ENABLE_OLD_SERVICES) +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel() + { + asio::error_code ec; + std::size_t s = this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Cancel any asynchronous + /// operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel(asio::error_code& ec) + { + return this->get_service().cancel(this->get_implementation(), ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Cancels one asynchronous operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one() + { + asio::error_code ec; + std::size_t s = this->get_service().cancel_one( + this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel_one"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Cancels one asynchronous + /// operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one(asio::error_code& ec) + { + return this->get_service().cancel_one(this->get_implementation(), ec); + } + + /// (Deprecated: Use expiry().) Get the timer's expiry time as an absolute + /// time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_point expires_at() const + { + return this->get_service().expires_at(this->get_implementation()); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the timer's expiry time as an absolute time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_point expiry() const + { + return this->get_service().expiry(this->get_implementation()); + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_point& expiry_time) + { + asio::error_code ec; + std::size_t s = this->get_service().expires_at( + this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Set the timer's expiry time as + /// an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_point& expiry_time, + asio::error_code& ec) + { + return this->get_service().expires_at( + this->get_implementation(), expiry_time, ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_after() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_after(const duration& expiry_time) + { + asio::error_code ec; + std::size_t s = this->get_service().expires_after( + this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_after"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the timer's expiry time relative to now. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + duration expires_from_now() const + { + return this->get_service().expires_from_now(this->get_implementation()); + } + + /// (Deprecated: Use expires_after().) Set the timer's expiry time relative + /// to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration& expiry_time) + { + asio::error_code ec; + std::size_t s = this->get_service().expires_from_now( + this->get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + return s; + } + + /// (Deprecated: Use expires_after().) Set the timer's expiry time relative + /// to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration& expiry_time, + asio::error_code& ec) + { + return this->get_service().expires_from_now( + this->get_implementation(), expiry_time, ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @throws asio::system_error Thrown on failure. + */ + void wait() + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @param ec Set to indicate what error occurred, if any. + */ + void wait(asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), ec); + } + + /// Start an asynchronous wait on the timer. + /** + * This function may be used to initiate an asynchronous wait against the + * timer. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li The timer has expired. + * + * @li The timer was cancelled, in which case the handler is passed the error + * code asio::error::operation_aborted. + * + * @param handler The handler to be called when the timer expires. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(ASIO_MOVE_ARG(WaitHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_wait(this->get_implementation(), + ASIO_MOVE_CAST(WaitHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + async_completion init(handler); + + this->get_service().async_wait(this->get_implementation(), + init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + +private: + // Disallow copying and assignment. + basic_waitable_timer(const basic_waitable_timer&) ASIO_DELETED; + basic_waitable_timer& operator=( + const basic_waitable_timer&) ASIO_DELETED; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# undef ASIO_SVC_T +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_BASIC_WAITABLE_TIMER_HPP diff --git a/subprojects/asio/include/asio/bind_executor.hpp b/subprojects/asio/include/asio/bind_executor.hpp new file mode 100644 index 000000000..9e2094b54 --- /dev/null +++ b/subprojects/asio/include/asio/bind_executor.hpp @@ -0,0 +1,611 @@ +// +// bind_executor.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BIND_EXECUTOR_HPP +#define ASIO_BIND_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" +#include "asio/associated_executor.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/async_result.hpp" +#include "asio/execution_context.hpp" +#include "asio/is_executor.hpp" +#include "asio/uses_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct executor_binder_check +{ + typedef void type; +}; + +// Helper to automatically define nested typedef result_type. + +template +struct executor_binder_result_type +{ +protected: + typedef void result_type_or_void; +}; + +template +struct executor_binder_result_type::type> +{ + typedef typename T::result_type result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +// Helper to automatically define nested typedef argument_type. + +template +struct executor_binder_argument_type {}; + +template +struct executor_binder_argument_type::type> +{ + typedef typename T::argument_type argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 argument_type; +}; + +// Helper to automatically define nested typedefs first_argument_type and +// second_argument_type. + +template +struct executor_binder_argument_types {}; + +template +struct executor_binder_argument_types::type> +{ + typedef typename T::first_argument_type first_argument_type; + typedef typename T::second_argument_type second_argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 first_argument_type; + typedef A2 second_argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 first_argument_type; + typedef A2 second_argument_type; +}; + +// Helper to: +// - Apply the empty base optimisation to the executor. +// - Perform uses_executor construction of the target type, if required. + +template +class executor_binder_base; + +template +class executor_binder_base + : protected Executor +{ +protected: + template + executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u) + : executor_(ASIO_MOVE_CAST(E)(e)), + target_(executor_arg_t(), executor_, ASIO_MOVE_CAST(U)(u)) + { + } + + Executor executor_; + T target_; +}; + +template +class executor_binder_base +{ +protected: + template + executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u) + : executor_(ASIO_MOVE_CAST(E)(e)), + target_(ASIO_MOVE_CAST(U)(u)) + { + } + + Executor executor_; + T target_; +}; + +// Helper to enable SFINAE on zero-argument operator() below. + +template +struct executor_binder_result_of0 +{ + typedef void type; +}; + +template +struct executor_binder_result_of0::type>::type> +{ + typedef typename result_of::type type; +}; + +} // namespace detail + +/// A call wrapper type to bind an executor of type @c Executor to an object of +/// type @c T. +template +class executor_binder +#if !defined(GENERATING_DOCUMENTATION) + : public detail::executor_binder_result_type, + public detail::executor_binder_argument_type, + public detail::executor_binder_argument_types, + private detail::executor_binder_base< + T, Executor, uses_executor::value> +#endif // !defined(GENERATING_DOCUMENTATION) +{ +public: + /// The type of the target object. + typedef T target_type; + + /// The type of the associated executor. + typedef Executor executor_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The return type if a function. + /** + * The type of @c result_type is based on the type @c T of the wrapper's + * target object: + * + * @li if @c T is a pointer to function type, @c result_type is a synonym for + * the return type of @c T; + * + * @li if @c T is a class type with a member type @c result_type, then @c + * result_type is a synonym for @c T::result_type; + * + * @li otherwise @c result_type is not defined. + */ + typedef see_below result_type; + + /// The type of the function's argument. + /** + * The type of @c argument_type is based on the type @c T of the wrapper's + * target object: + * + * @li if @c T is a pointer to a function type accepting a single argument, + * @c argument_type is a synonym for the return type of @c T; + * + * @li if @c T is a class type with a member type @c argument_type, then @c + * argument_type is a synonym for @c T::argument_type; + * + * @li otherwise @c argument_type is not defined. + */ + typedef see_below argument_type; + + /// The type of the function's first argument. + /** + * The type of @c first_argument_type is based on the type @c T of the + * wrapper's target object: + * + * @li if @c T is a pointer to a function type accepting two arguments, @c + * first_argument_type is a synonym for the return type of @c T; + * + * @li if @c T is a class type with a member type @c first_argument_type, + * then @c first_argument_type is a synonym for @c T::first_argument_type; + * + * @li otherwise @c first_argument_type is not defined. + */ + typedef see_below first_argument_type; + + /// The type of the function's second argument. + /** + * The type of @c second_argument_type is based on the type @c T of the + * wrapper's target object: + * + * @li if @c T is a pointer to a function type accepting two arguments, @c + * second_argument_type is a synonym for the return type of @c T; + * + * @li if @c T is a class type with a member type @c first_argument_type, + * then @c second_argument_type is a synonym for @c T::second_argument_type; + * + * @li otherwise @c second_argument_type is not defined. + */ + typedef see_below second_argument_type; +#endif // defined(GENERATING_DOCUMENTATION) + + /// Construct an executor wrapper for the specified object. + /** + * This constructor is only valid if the type @c T is constructible from type + * @c U. + */ + template + executor_binder(executor_arg_t, const executor_type& e, + ASIO_MOVE_ARG(U) u) + : base_type(e, ASIO_MOVE_CAST(U)(u)) + { + } + + /// Copy constructor. + executor_binder(const executor_binder& other) + : base_type(other.get_executor(), other.get()) + { + } + + /// Construct a copy, but specify a different executor. + executor_binder(executor_arg_t, const executor_type& e, + const executor_binder& other) + : base_type(e, other.get()) + { + } + + /// Construct a copy of a different executor wrapper type. + /** + * This constructor is only valid if the @c Executor type is constructible + * from type @c OtherExecutor, and the type @c T is constructible from type + * @c U. + */ + template + executor_binder(const executor_binder& other) + : base_type(other.get_executor(), other.get()) + { + } + + /// Construct a copy of a different executor wrapper type, but specify a + /// different executor. + /** + * This constructor is only valid if the type @c T is constructible from type + * @c U. + */ + template + executor_binder(executor_arg_t, const executor_type& e, + const executor_binder& other) + : base_type(e, other.get()) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Move constructor. + executor_binder(executor_binder&& other) + : base_type(ASIO_MOVE_CAST(executor_type)(other.get_executor()), + ASIO_MOVE_CAST(T)(other.get())) + { + } + + /// Move construct the target object, but specify a different executor. + executor_binder(executor_arg_t, const executor_type& e, + executor_binder&& other) + : base_type(e, ASIO_MOVE_CAST(T)(other.get())) + { + } + + /// Move construct from a different executor wrapper type. + template + executor_binder(executor_binder&& other) + : base_type(ASIO_MOVE_CAST(OtherExecutor)(other.get_executor()), + ASIO_MOVE_CAST(U)(other.get())) + { + } + + /// Move construct from a different executor wrapper type, but specify a + /// different executor. + template + executor_binder(executor_arg_t, const executor_type& e, + executor_binder&& other) + : base_type(e, ASIO_MOVE_CAST(U)(other.get())) + { + } + +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor. + ~executor_binder() + { + } + + /// Obtain a reference to the target object. + target_type& get() ASIO_NOEXCEPT + { + return this->target_; + } + + /// Obtain a reference to the target object. + const target_type& get() const ASIO_NOEXCEPT + { + return this->target_; + } + + /// Obtain the associated executor. + executor_type get_executor() const ASIO_NOEXCEPT + { + return this->executor_; + } + +#if defined(GENERATING_DOCUMENTATION) + + template auto operator()(Args&& ...); + template auto operator()(Args&& ...) const; + +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + + /// Forwarding function call operator. + template + typename result_of::type operator()( + ASIO_MOVE_ARG(Args)... args) + { + return this->target_(ASIO_MOVE_CAST(Args)(args)...); + } + + /// Forwarding function call operator. + template + typename result_of::type operator()( + ASIO_MOVE_ARG(Args)... args) const + { + return this->target_(ASIO_MOVE_CAST(Args)(args)...); + } + +#elif defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER) + + typename detail::executor_binder_result_of0::type operator()() + { + return this->target_(); + } + + typename detail::executor_binder_result_of0::type operator()() const + { + return this->target_(); + } + +#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \ + template \ + typename result_of::type operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + \ + template \ + typename result_of::type operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) const \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF) +#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF + +#else // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER) + + typedef typename detail::executor_binder_result_type::result_type_or_void + result_type_or_void; + + result_type_or_void operator()() + { + return this->target_(); + } + + result_type_or_void operator()() const + { + return this->target_(); + } + +#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \ + template \ + result_type_or_void operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + \ + template \ + result_type_or_void operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) const \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF) +#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF + +#endif // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER) + +private: + typedef detail::executor_binder_base::value> base_type; +}; + +/// Associate an object of type @c T with an executor of type @c Executor. +template +inline executor_binder::type, Executor> +bind_executor(const Executor& ex, ASIO_MOVE_ARG(T) t, + typename enable_if::value>::type* = 0) +{ + return executor_binder::type, Executor>( + executor_arg_t(), ex, ASIO_MOVE_CAST(T)(t)); +} + +/// Associate an object of type @c T with an execution context's executor. +template +inline executor_binder::type, + typename ExecutionContext::executor_type> +bind_executor(ExecutionContext& ctx, ASIO_MOVE_ARG(T) t, + typename enable_if::value>::type* = 0) +{ + return executor_binder::type, + typename ExecutionContext::executor_type>( + executor_arg_t(), ctx.get_executor(), ASIO_MOVE_CAST(T)(t)); +} + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct uses_executor, Executor> + : true_type {}; + +template +class async_result, Signature> +{ +public: + typedef executor_binder< + typename async_result::completion_handler_type, Executor> + completion_handler_type; + + typedef typename async_result::return_type return_type; + + explicit async_result(executor_binder& b) + : target_(b.get()) + { + } + + return_type get() + { + return target_.get(); + } + +private: + async_result(const async_result&) ASIO_DELETED; + async_result& operator=(const async_result&) ASIO_DELETED; + + async_result target_; +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template +struct handler_type, Signature> +{ + typedef executor_binder< + typename handler_type::type, Executor> type; +}; + +template +class async_result > +{ +public: + typedef typename async_result::type type; + + explicit async_result(executor_binder& b) + : target_(b.get()) + { + } + + type get() + { + return target_.get(); + } + +private: + async_result target_; +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const executor_binder& b, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(b.get(), a); + } +}; + +template +struct associated_executor, Executor1> +{ + typedef Executor type; + + static type get(const executor_binder& b, + const Executor1& = Executor1()) ASIO_NOEXCEPT + { + return b.get_executor(); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BIND_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/buffer.hpp b/subprojects/asio/include/asio/buffer.hpp new file mode 100644 index 000000000..56a14880d --- /dev/null +++ b/subprojects/asio/include/asio/buffer.hpp @@ -0,0 +1,2169 @@ +// +// buffer.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFER_HPP +#define ASIO_BUFFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/string_view.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/detail/type_traits.hpp" + +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1700) +# if defined(_HAS_ITERATOR_DEBUGGING) && (_HAS_ITERATOR_DEBUGGING != 0) +# if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# define ASIO_ENABLE_BUFFER_DEBUGGING +# endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# endif // defined(_HAS_ITERATOR_DEBUGGING) +#endif // defined(ASIO_MSVC) && (ASIO_MSVC >= 1700) + +#if defined(__GNUC__) +# if defined(_GLIBCXX_DEBUG) +# if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# define ASIO_ENABLE_BUFFER_DEBUGGING +# endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# endif // defined(_GLIBCXX_DEBUG) +#endif // defined(__GNUC__) + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) +# include "asio/detail/functional.hpp" +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +#if defined(ASIO_HAS_BOOST_WORKAROUND) +# include +# if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) \ + || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) +# define ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND +# endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) + // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) +#endif // defined(ASIO_HAS_BOOST_WORKAROUND) + +#if defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) +# include "asio/detail/type_traits.hpp" +#endif // defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class mutable_buffer; +class const_buffer; + +/// Holds a buffer that can be modified. +/** + * The mutable_buffer class provides a safe representation of a buffer that can + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + * + * @par Accessing Buffer Contents + * + * The contents of a buffer may be accessed using the @c data() and @c size() + * member functions: + * + * @code asio::mutable_buffer b1 = ...; + * std::size_t s1 = b1.size(); + * unsigned char* p1 = static_cast(b1.data()); + * @endcode + * + * The @c data() member function permits violations of type safety, so uses of + * it in application code should be carefully considered. + */ +class mutable_buffer +{ +public: + /// Construct an empty buffer. + mutable_buffer() ASIO_NOEXCEPT + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + mutable_buffer(void* data, std::size_t size) ASIO_NOEXCEPT + : data_(data), + size_(size) + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + mutable_buffer(void* data, std::size_t size, + asio::detail::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const asio::detail::function& get_debug_check() const + { + return debug_check_; + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Get a pointer to the beginning of the memory range. + void* data() const ASIO_NOEXCEPT + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (size_ && debug_check_) + debug_check_(); +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + return data_; + } + + /// Get the size of the memory range. + std::size_t size() const ASIO_NOEXCEPT + { + return size_; + } + + /// Move the start of the buffer by the specified number of bytes. + mutable_buffer& operator+=(std::size_t n) ASIO_NOEXCEPT + { + std::size_t offset = n < size_ ? n : size_; + data_ = static_cast(data_) + offset; + size_ -= offset; + return *this; + } + +private: + void* data_; + std::size_t size_; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + asio::detail::function debug_check_; +#endif // ASIO_ENABLE_BUFFER_DEBUGGING +}; + +#if !defined(ASIO_NO_DEPRECATED) + +/// (Deprecated: Use mutable_buffer.) Adapts a single modifiable buffer so that +/// it meets the requirements of the MutableBufferSequence concept. +class mutable_buffers_1 + : public mutable_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Construct to represent a given memory range. + mutable_buffers_1(void* data, std::size_t size) ASIO_NOEXCEPT + : mutable_buffer(data, size) + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + mutable_buffers_1(void* data, std::size_t size, + asio::detail::function debug_check) + : mutable_buffer(data, size, debug_check) + { + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Construct to represent a single modifiable buffer. + explicit mutable_buffers_1(const mutable_buffer& b) ASIO_NOEXCEPT + : mutable_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const ASIO_NOEXCEPT + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const ASIO_NOEXCEPT + { + return begin() + 1; + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Holds a buffer that cannot be modified. +/** + * The const_buffer class provides a safe representation of a buffer that cannot + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + * + * @par Accessing Buffer Contents + * + * The contents of a buffer may be accessed using the @c data() and @c size() + * member functions: + * + * @code asio::const_buffer b1 = ...; + * std::size_t s1 = b1.size(); + * const unsigned char* p1 = static_cast(b1.data()); + * @endcode + * + * The @c data() member function permits violations of type safety, so uses of + * it in application code should be carefully considered. + */ +class const_buffer +{ +public: + /// Construct an empty buffer. + const_buffer() ASIO_NOEXCEPT + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + const_buffer(const void* data, std::size_t size) ASIO_NOEXCEPT + : data_(data), + size_(size) + { + } + + /// Construct a non-modifiable buffer from a modifiable one. + const_buffer(const mutable_buffer& b) ASIO_NOEXCEPT + : data_(b.data()), + size_(b.size()) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , debug_check_(b.get_debug_check()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + const_buffer(const void* data, std::size_t size, + asio::detail::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const asio::detail::function& get_debug_check() const + { + return debug_check_; + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Get a pointer to the beginning of the memory range. + const void* data() const ASIO_NOEXCEPT + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (size_ && debug_check_) + debug_check_(); +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + return data_; + } + + /// Get the size of the memory range. + std::size_t size() const ASIO_NOEXCEPT + { + return size_; + } + + /// Move the start of the buffer by the specified number of bytes. + const_buffer& operator+=(std::size_t n) ASIO_NOEXCEPT + { + std::size_t offset = n < size_ ? n : size_; + data_ = static_cast(data_) + offset; + size_ -= offset; + return *this; + } + +private: + const void* data_; + std::size_t size_; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + asio::detail::function debug_check_; +#endif // ASIO_ENABLE_BUFFER_DEBUGGING +}; + +#if !defined(ASIO_NO_DEPRECATED) + +/// (Deprecated: Use const_buffer.) Adapts a single non-modifiable buffer so +/// that it meets the requirements of the ConstBufferSequence concept. +class const_buffers_1 + : public const_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef const_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const const_buffer* const_iterator; + + /// Construct to represent a given memory range. + const_buffers_1(const void* data, std::size_t size) ASIO_NOEXCEPT + : const_buffer(data, size) + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + const_buffers_1(const void* data, std::size_t size, + asio::detail::function debug_check) + : const_buffer(data, size, debug_check) + { + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Construct to represent a single non-modifiable buffer. + explicit const_buffers_1(const const_buffer& b) ASIO_NOEXCEPT + : const_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const ASIO_NOEXCEPT + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const ASIO_NOEXCEPT + { + return begin() + 1; + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +/// (Deprecated: Use the socket/descriptor wait() and async_wait() member +/// functions.) An implementation of both the ConstBufferSequence and +/// MutableBufferSequence concepts to represent a null buffer sequence. +class null_buffers +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Get a random-access iterator to the first element. + const_iterator begin() const ASIO_NOEXCEPT + { + return &buf_; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const ASIO_NOEXCEPT + { + return &buf_; + } + +private: + mutable_buffer buf_; +}; + +/** @defgroup buffer_sequence_begin asio::buffer_sequence_begin + * + * @brief The asio::buffer_sequence_begin function returns an iterator + * pointing to the first element in a buffer sequence. + */ +/*@{*/ + +/// Get an iterator to the first element in a buffer sequence. +inline const mutable_buffer* buffer_sequence_begin(const mutable_buffer& b) +{ + return &b; +} + +/// Get an iterator to the first element in a buffer sequence. +inline const const_buffer* buffer_sequence_begin(const const_buffer& b) +{ + return &b; +} + +#if defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/// Get an iterator to the first element in a buffer sequence. +template +inline auto buffer_sequence_begin(C& c) -> decltype(c.begin()) +{ + return c.begin(); +} + +/// Get an iterator to the first element in a buffer sequence. +template +inline auto buffer_sequence_begin(const C& c) -> decltype(c.begin()) +{ + return c.begin(); +} + +#else // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +template +inline typename C::iterator buffer_sequence_begin(C& c) +{ + return c.begin(); +} + +template +inline typename C::const_iterator buffer_sequence_begin(const C& c) +{ + return c.begin(); +} + +#endif // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/*@}*/ + +/** @defgroup buffer_sequence_end asio::buffer_sequence_end + * + * @brief The asio::buffer_sequence_end function returns an iterator + * pointing to one past the end element in a buffer sequence. + */ +/*@{*/ + +/// Get an iterator to one past the end element in a buffer sequence. +inline const mutable_buffer* buffer_sequence_end(const mutable_buffer& b) +{ + return &b + 1; +} + +/// Get an iterator to one past the end element in a buffer sequence. +inline const const_buffer* buffer_sequence_end(const const_buffer& b) +{ + return &b + 1; +} + +#if defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/// Get an iterator to one past the end element in a buffer sequence. +template +inline auto buffer_sequence_end(C& c) -> decltype(c.end()) +{ + return c.end(); +} + +/// Get an iterator to one past the end element in a buffer sequence. +template +inline auto buffer_sequence_end(const C& c) -> decltype(c.end()) +{ + return c.end(); +} + +#else // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +template +inline typename C::iterator buffer_sequence_end(C& c) +{ + return c.end(); +} + +template +inline typename C::const_iterator buffer_sequence_end(const C& c) +{ + return c.end(); +} + +#endif // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/*@}*/ + +namespace detail { + +// Tag types used to select appropriately optimised overloads. +struct one_buffer {}; +struct multiple_buffers {}; + +// Helper trait to detect single buffers. +template +struct buffer_sequence_cardinality : + conditional< + is_same::value +#if !defined(ASIO_NO_DEPRECATED) + || is_same::value + || is_same::value +#endif // !defined(ASIO_NO_DEPRECATED) + || is_same::value, + one_buffer, multiple_buffers>::type {}; + +template +inline std::size_t buffer_size(one_buffer, + Iterator begin, Iterator) ASIO_NOEXCEPT +{ + return const_buffer(*begin).size(); +} + +template +inline std::size_t buffer_size(multiple_buffers, + Iterator begin, Iterator end) ASIO_NOEXCEPT +{ + std::size_t total_buffer_size = 0; + + Iterator iter = begin; + for (; iter != end; ++iter) + { + const_buffer b(*iter); + total_buffer_size += b.size(); + } + + return total_buffer_size; +} + +} // namespace detail + +/// Get the total number of bytes in a buffer sequence. +/** + * The @c buffer_size function determines the total size of all buffers in the + * buffer sequence, as if computed as follows: + * + * @code size_t total_size = 0; + * auto i = asio::buffer_sequence_begin(buffers); + * auto end = asio::buffer_sequence_end(buffers); + * for (; i != end; ++i) + * { + * const_buffer b(*i); + * total_size += b.size(); + * } + * return total_size; @endcode + * + * The @c BufferSequence template parameter may meet either of the @c + * ConstBufferSequence or @c MutableBufferSequence type requirements. + */ +template +inline std::size_t buffer_size(const BufferSequence& b) ASIO_NOEXCEPT +{ + return detail::buffer_size( + detail::buffer_sequence_cardinality(), + asio::buffer_sequence_begin(b), + asio::buffer_sequence_end(b)); +} + +#if !defined(ASIO_NO_DEPRECATED) + +/** @defgroup buffer_cast asio::buffer_cast + * + * @brief (Deprecated: Use the @c data() member function.) The + * asio::buffer_cast function is used to obtain a pointer to the + * underlying memory region associated with a buffer. + * + * @par Examples: + * + * To access the memory of a non-modifiable buffer, use: + * @code asio::const_buffer b1 = ...; + * const unsigned char* p1 = asio::buffer_cast(b1); + * @endcode + * + * To access the memory of a modifiable buffer, use: + * @code asio::mutable_buffer b2 = ...; + * unsigned char* p2 = asio::buffer_cast(b2); + * @endcode + * + * The asio::buffer_cast function permits violations of type safety, so + * uses of it in application code should be carefully considered. + */ +/*@{*/ + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +template +inline PointerToPodType buffer_cast(const mutable_buffer& b) ASIO_NOEXCEPT +{ + return static_cast(b.data()); +} + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +template +inline PointerToPodType buffer_cast(const const_buffer& b) ASIO_NOEXCEPT +{ + return static_cast(b.data()); +} + +/*@}*/ + +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(const mutable_buffer& b, + std::size_t n) ASIO_NOEXCEPT +{ + std::size_t offset = n < b.size() ? n : b.size(); + char* new_data = static_cast(b.data()) + offset; + std::size_t new_size = b.size() - offset; + return mutable_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(std::size_t n, + const mutable_buffer& b) ASIO_NOEXCEPT +{ + return b + n; +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(const const_buffer& b, + std::size_t n) ASIO_NOEXCEPT +{ + std::size_t offset = n < b.size() ? n : b.size(); + const char* new_data = static_cast(b.data()) + offset; + std::size_t new_size = b.size() - offset; + return const_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(std::size_t n, + const const_buffer& b) ASIO_NOEXCEPT +{ + return b + n; +} + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) +namespace detail { + +template +class buffer_debug_check +{ +public: + buffer_debug_check(Iterator iter) + : iter_(iter) + { + } + + ~buffer_debug_check() + { +#if defined(ASIO_MSVC) && (ASIO_MSVC == 1400) + // MSVC 8's string iterator checking may crash in a std::string::iterator + // object's destructor when the iterator points to an already-destroyed + // std::string object, unless the iterator is cleared first. + iter_ = Iterator(); +#endif // defined(ASIO_MSVC) && (ASIO_MSVC == 1400) + } + + void operator()() + { + (void)*iter_; + } + +private: + Iterator iter_; +}; + +} // namespace detail +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +/** @defgroup buffer asio::buffer + * + * @brief The asio::buffer function is used to create a buffer object to + * represent raw memory, an array of POD elements, a vector of POD elements, + * or a std::string. + * + * A buffer object represents a contiguous region of memory as a 2-tuple + * consisting of a pointer and size in bytes. A tuple of the form {void*, + * size_t} specifies a mutable (modifiable) region of memory. Similarly, a + * tuple of the form {const void*, size_t} specifies a const + * (non-modifiable) region of memory. These two forms correspond to the classes + * mutable_buffer and const_buffer, respectively. To mirror C++'s conversion + * rules, a mutable_buffer is implicitly convertible to a const_buffer, and the + * opposite conversion is not permitted. + * + * The simplest use case involves reading or writing a single buffer of a + * specified size: + * + * @code sock.send(asio::buffer(data, size)); @endcode + * + * In the above example, the return value of asio::buffer meets the + * requirements of the ConstBufferSequence concept so that it may be directly + * passed to the socket's write function. A buffer created for modifiable + * memory also meets the requirements of the MutableBufferSequence concept. + * + * An individual buffer may be created from a builtin array, std::vector, + * std::array or boost::array of POD elements. This helps prevent buffer + * overruns by automatically determining the size of the buffer: + * + * @code char d1[128]; + * size_t bytes_transferred = sock.receive(asio::buffer(d1)); + * + * std::vector d2(128); + * bytes_transferred = sock.receive(asio::buffer(d2)); + * + * std::array d3; + * bytes_transferred = sock.receive(asio::buffer(d3)); + * + * boost::array d4; + * bytes_transferred = sock.receive(asio::buffer(d4)); @endcode + * + * In all three cases above, the buffers created are exactly 128 bytes long. + * Note that a vector is @e never automatically resized when creating or using + * a buffer. The buffer size is determined using the vector's size() + * member function, and not its capacity. + * + * @par Accessing Buffer Contents + * + * The contents of a buffer may be accessed using the @c data() and @c size() + * member functions: + * + * @code asio::mutable_buffer b1 = ...; + * std::size_t s1 = b1.size(); + * unsigned char* p1 = static_cast(b1.data()); + * + * asio::const_buffer b2 = ...; + * std::size_t s2 = b2.size(); + * const void* p2 = b2.data(); @endcode + * + * The @c data() member function permits violations of type safety, so + * uses of it in application code should be carefully considered. + * + * For convenience, a @ref buffer_size function is provided that works with + * both buffers and buffer sequences (that is, types meeting the + * ConstBufferSequence or MutableBufferSequence type requirements). In this + * case, the function returns the total size of all buffers in the sequence. + * + * @par Buffer Copying + * + * The @ref buffer_copy function may be used to copy raw bytes between + * individual buffers and buffer sequences. +* + * In particular, when used with the @ref buffer_size function, the @ref + * buffer_copy function can be used to linearise a sequence of buffers. For + * example: + * + * @code vector buffers = ...; + * + * vector data(asio::buffer_size(buffers)); + * asio::buffer_copy(asio::buffer(data), buffers); @endcode + * + * Note that @ref buffer_copy is implemented in terms of @c memcpy, and + * consequently it cannot be used to copy between overlapping memory regions. + * + * @par Buffer Invalidation + * + * A buffer object does not have any ownership of the memory it refers to. It + * is the responsibility of the application to ensure the memory region remains + * valid until it is no longer required for an I/O operation. When the memory + * is no longer available, the buffer is said to have been invalidated. + * + * For the asio::buffer overloads that accept an argument of type + * std::vector, the buffer objects returned are invalidated by any vector + * operation that also invalidates all references, pointers and iterators + * referring to the elements in the sequence (C++ Std, 23.2.4) + * + * For the asio::buffer overloads that accept an argument of type + * std::basic_string, the buffer objects returned are invalidated according to + * the rules defined for invalidation of references, pointers and iterators + * referring to elements of the sequence (C++ Std, 21.3). + * + * @par Buffer Arithmetic + * + * Buffer objects may be manipulated using simple arithmetic in a safe way + * which helps prevent buffer overruns. Consider an array initialised as + * follows: + * + * @code boost::array a = { 'a', 'b', 'c', 'd', 'e' }; @endcode + * + * A buffer object @c b1 created using: + * + * @code b1 = asio::buffer(a); @endcode + * + * represents the entire array, { 'a', 'b', 'c', 'd', 'e' }. An + * optional second argument to the asio::buffer function may be used to + * limit the size, in bytes, of the buffer: + * + * @code b2 = asio::buffer(a, 3); @endcode + * + * such that @c b2 represents the data { 'a', 'b', 'c' }. Even if the + * size argument exceeds the actual size of the array, the size of the buffer + * object created will be limited to the array size. + * + * An offset may be applied to an existing buffer to create a new one: + * + * @code b3 = b1 + 2; @endcode + * + * where @c b3 will set to represent { 'c', 'd', 'e' }. If the offset + * exceeds the size of the existing buffer, the newly created buffer will be + * empty. + * + * Both an offset and size may be specified to create a buffer that corresponds + * to a specific range of bytes within an existing buffer: + * + * @code b4 = asio::buffer(b1 + 1, 3); @endcode + * + * so that @c b4 will refer to the bytes { 'b', 'c', 'd' }. + * + * @par Buffers and Scatter-Gather I/O + * + * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple + * buffer objects may be assigned into a container that supports the + * MutableBufferSequence (for read) or ConstBufferSequence (for write) concepts: + * + * @code + * char d1[128]; + * std::vector d2(128); + * boost::array d3; + * + * boost::array bufs1 = { + * asio::buffer(d1), + * asio::buffer(d2), + * asio::buffer(d3) }; + * bytes_transferred = sock.receive(bufs1); + * + * std::vector bufs2; + * bufs2.push_back(asio::buffer(d1)); + * bufs2.push_back(asio::buffer(d2)); + * bufs2.push_back(asio::buffer(d3)); + * bytes_transferred = sock.send(bufs2); @endcode + */ +/*@{*/ + +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +# define ASIO_MUTABLE_BUFFER mutable_buffer +# define ASIO_CONST_BUFFER const_buffer +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +# define ASIO_MUTABLE_BUFFER mutable_buffers_1 +# define ASIO_CONST_BUFFER const_buffers_1 +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer from an existing buffer. +/** + * @returns mutable_buffer(b). + */ +inline ASIO_MUTABLE_BUFFER buffer( + const mutable_buffer& b) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(b); +} + +/// Create a new modifiable buffer from an existing buffer. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * b.data(), + * min(b.size(), max_size_in_bytes)); @endcode + */ +inline ASIO_MUTABLE_BUFFER buffer(const mutable_buffer& b, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER( + mutable_buffer(b.data(), + b.size() < max_size_in_bytes + ? b.size() : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new non-modifiable buffer from an existing buffer. +/** + * @returns const_buffer(b). + */ +inline ASIO_CONST_BUFFER buffer( + const const_buffer& b) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(b); +} + +/// Create a new non-modifiable buffer from an existing buffer. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * b.data(), + * min(b.size(), max_size_in_bytes)); @endcode + */ +inline ASIO_CONST_BUFFER buffer(const const_buffer& b, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(b.data(), + b.size() < max_size_in_bytes + ? b.size() : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given memory range. +/** + * @returns mutable_buffer(data, size_in_bytes). + */ +inline ASIO_MUTABLE_BUFFER buffer(void* data, + std::size_t size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data, size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given memory range. +/** + * @returns const_buffer(data, size_in_bytes). + */ +inline ASIO_CONST_BUFFER buffer(const void* data, + std::size_t size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data, size_in_bytes); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * static_cast(data), + * N * sizeof(PodType)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(PodType (&data)[N]) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data, N * sizeof(PodType)); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * static_cast(data), + * min(N * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(PodType (&data)[N], + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data, + N * sizeof(PodType) < max_size_in_bytes + ? N * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * static_cast(data), + * N * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + const PodType (&data)[N]) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data, N * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * static_cast(data), + * min(N * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(const PodType (&data)[N], + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data, + N * sizeof(PodType) < max_size_in_bytes + ? N * sizeof(PodType) : max_size_in_bytes); +} + +#if defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +// Borland C++ and Sun Studio think the overloads: +// +// unspecified buffer(boost::array& array ...); +// +// and +// +// unspecified buffer(boost::array& array ...); +// +// are ambiguous. This will be worked around by using a buffer_types traits +// class that contains typedefs for the appropriate buffer and container +// classes, based on whether PodType is const or non-const. + +namespace detail { + +template +struct buffer_types_base; + +template <> +struct buffer_types_base +{ + typedef mutable_buffer buffer_type; + typedef ASIO_MUTABLE_BUFFER container_type; +}; + +template <> +struct buffer_types_base +{ + typedef const_buffer buffer_type; + typedef ASIO_CONST_BUFFER container_type; +}; + +template +struct buffer_types + : public buffer_types_base::value> +{ +}; + +} // namespace detail + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data) ASIO_NOEXCEPT +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), data.size() * sizeof(PodType))); +} + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes)); +} + +#else // defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + boost::array& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER( + data.c_array(), data.size() * sizeof(PodType)); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.c_array(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + boost::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +#endif // defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + const boost::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(const boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +#if defined(ASIO_HAS_STD_ARRAY) || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::array& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(std::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + std::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(std::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(const std::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +#endif // defined(ASIO_HAS_STD_ARRAY) || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * data.size() * sizeof(PodType)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::vector& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER( + data.size() ? &data[0] : 0, data.size() * sizeof(PodType) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_MUTABLE_BUFFER buffer(std::vector& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.size() ? &data[0] : 0, + * data.size() * sizeof(PodType)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::vector& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER( + data.size() ? &data[0] : 0, data.size() * sizeof(PodType) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::vector& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given string. +/** + * @returns mutable_buffer(data.size() ? &data[0] : 0, + * data.size() * sizeof(Elem)). + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::basic_string& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given string. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(Elem), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::basic_string& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) < max_size_in_bytes + ? data.size() * sizeof(Elem) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @returns const_buffer(data.data(), data.size() * sizeof(Elem)). + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::basic_string& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(Elem) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(Elem), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::basic_string& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(Elem) < max_size_in_bytes + ? data.size() * sizeof(Elem) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +#if defined(ASIO_HAS_STRING_VIEW) \ + || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer that represents the given string_view. +/** + * @returns mutable_buffer(data.size() ? &data[0] : 0, + * data.size() * sizeof(Elem)). + */ +template +inline ASIO_CONST_BUFFER buffer( + basic_string_view data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename basic_string_view::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(Elem), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + basic_string_view data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) < max_size_in_bytes + ? data.size() * sizeof(Elem) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename basic_string_view::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +#endif // defined(ASIO_HAS_STRING_VIEW) + // || defined(GENERATING_DOCUMENTATION) + +/*@}*/ + +/// Adapt a basic_string to the DynamicBuffer requirements. +/** + * Requires that sizeof(Elem) == 1. + */ +template +class dynamic_string_buffer +{ +public: + /// The type used to represent the input sequence as a list of buffers. + typedef ASIO_CONST_BUFFER const_buffers_type; + + /// The type used to represent the output sequence as a list of buffers. + typedef ASIO_MUTABLE_BUFFER mutable_buffers_type; + + /// Construct a dynamic buffer from a string. + /** + * @param s The string to be used as backing storage for the dynamic buffer. + * Any existing data in the string is treated as the dynamic buffer's input + * sequence. The object stores a reference to the string and the user is + * responsible for ensuring that the string object remains valid until the + * dynamic_string_buffer object is destroyed. + * + * @param maximum_size Specifies a maximum size for the buffer, in bytes. + */ + explicit dynamic_string_buffer(std::basic_string& s, + std::size_t maximum_size = + (std::numeric_limits::max)()) ASIO_NOEXCEPT + : string_(s), + size_(string_.size()), + max_size_(maximum_size) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move construct a dynamic buffer. + dynamic_string_buffer(dynamic_string_buffer&& other) ASIO_NOEXCEPT + : string_(other.string_), + size_(other.size_), + max_size_(other.max_size_) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get the size of the input sequence. + std::size_t size() const ASIO_NOEXCEPT + { + return size_; + } + + /// Get the maximum size of the dynamic buffer. + /** + * @returns The allowed maximum of the sum of the sizes of the input sequence + * and output sequence. + */ + std::size_t max_size() const ASIO_NOEXCEPT + { + return max_size_; + } + + /// Get the current capacity of the dynamic buffer. + /** + * @returns The current total capacity of the buffer, i.e. for both the input + * sequence and output sequence. + */ + std::size_t capacity() const ASIO_NOEXCEPT + { + return string_.capacity(); + } + + /// Get a list of buffers that represents the input sequence. + /** + * @returns An object of type @c const_buffers_type that satisfies + * ConstBufferSequence requirements, representing the basic_string memory in + * input sequence. + * + * @note The returned object is invalidated by any @c dynamic_string_buffer + * or @c basic_string member function that modifies the input sequence or + * output sequence. + */ + const_buffers_type data() const ASIO_NOEXCEPT + { + return const_buffers_type(asio::buffer(string_, size_)); + } + + /// Get a list of buffers that represents the output sequence, with the given + /// size. + /** + * Ensures that the output sequence can accommodate @c n bytes, resizing the + * basic_string object as necessary. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing basic_string memory + * at the start of the output sequence of size @c n. + * + * @throws std::length_error If size() + n > max_size(). + * + * @note The returned object is invalidated by any @c dynamic_string_buffer + * or @c basic_string member function that modifies the input sequence or + * output sequence. + */ + mutable_buffers_type prepare(std::size_t n) + { + if (size () > max_size() || max_size() - size() < n) + { + std::length_error ex("dynamic_string_buffer too long"); + asio::detail::throw_exception(ex); + } + + string_.resize(size_ + n); + + return asio::buffer(asio::buffer(string_) + size_, n); + } + + /// Move bytes from the output sequence to the input sequence. + /** + * @param n The number of bytes to append from the start of the output + * sequence to the end of the input sequence. The remainder of the output + * sequence is discarded. + * + * Requires a preceding call prepare(x) where x >= n, and + * no intervening operations that modify the input or output sequence. + * + * @note If @c n is greater than the size of the output sequence, the entire + * output sequence is moved to the input sequence and no error is issued. + */ + void commit(std::size_t n) + { + size_ += (std::min)(n, string_.size() - size_); + string_.resize(size_); + } + + /// Remove characters from the input sequence. + /** + * Removes @c n characters from the beginning of the input sequence. + * + * @note If @c n is greater than the size of the input sequence, the entire + * input sequence is consumed and no error is issued. + */ + void consume(std::size_t n) + { + std::size_t consume_length = (std::min)(n, size_); + string_.erase(0, consume_length); + size_ -= consume_length; + } + +private: + std::basic_string& string_; + std::size_t size_; + const std::size_t max_size_; +}; + +/// Adapt a vector to the DynamicBuffer requirements. +/** + * Requires that sizeof(Elem) == 1. + */ +template +class dynamic_vector_buffer +{ +public: + /// The type used to represent the input sequence as a list of buffers. + typedef ASIO_CONST_BUFFER const_buffers_type; + + /// The type used to represent the output sequence as a list of buffers. + typedef ASIO_MUTABLE_BUFFER mutable_buffers_type; + + /// Construct a dynamic buffer from a string. + /** + * @param v The vector to be used as backing storage for the dynamic buffer. + * Any existing data in the vector is treated as the dynamic buffer's input + * sequence. The object stores a reference to the vector and the user is + * responsible for ensuring that the vector object remains valid until the + * dynamic_vector_buffer object is destroyed. + * + * @param maximum_size Specifies a maximum size for the buffer, in bytes. + */ + explicit dynamic_vector_buffer(std::vector& v, + std::size_t maximum_size = + (std::numeric_limits::max)()) ASIO_NOEXCEPT + : vector_(v), + size_(vector_.size()), + max_size_(maximum_size) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move construct a dynamic buffer. + dynamic_vector_buffer(dynamic_vector_buffer&& other) ASIO_NOEXCEPT + : vector_(other.vector_), + size_(other.size_), + max_size_(other.max_size_) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get the size of the input sequence. + std::size_t size() const ASIO_NOEXCEPT + { + return size_; + } + + /// Get the maximum size of the dynamic buffer. + /** + * @returns The allowed maximum of the sum of the sizes of the input sequence + * and output sequence. + */ + std::size_t max_size() const ASIO_NOEXCEPT + { + return max_size_; + } + + /// Get the current capacity of the dynamic buffer. + /** + * @returns The current total capacity of the buffer, i.e. for both the input + * sequence and output sequence. + */ + std::size_t capacity() const ASIO_NOEXCEPT + { + return vector_.capacity(); + } + + /// Get a list of buffers that represents the input sequence. + /** + * @returns An object of type @c const_buffers_type that satisfies + * ConstBufferSequence requirements, representing the basic_string memory in + * input sequence. + * + * @note The returned object is invalidated by any @c dynamic_vector_buffer + * or @c basic_string member function that modifies the input sequence or + * output sequence. + */ + const_buffers_type data() const ASIO_NOEXCEPT + { + return const_buffers_type(asio::buffer(vector_, size_)); + } + + /// Get a list of buffers that represents the output sequence, with the given + /// size. + /** + * Ensures that the output sequence can accommodate @c n bytes, resizing the + * basic_string object as necessary. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing basic_string memory + * at the start of the output sequence of size @c n. + * + * @throws std::length_error If size() + n > max_size(). + * + * @note The returned object is invalidated by any @c dynamic_vector_buffer + * or @c basic_string member function that modifies the input sequence or + * output sequence. + */ + mutable_buffers_type prepare(std::size_t n) + { + if (size () > max_size() || max_size() - size() < n) + { + std::length_error ex("dynamic_vector_buffer too long"); + asio::detail::throw_exception(ex); + } + + vector_.resize(size_ + n); + + return asio::buffer(asio::buffer(vector_) + size_, n); + } + + /// Move bytes from the output sequence to the input sequence. + /** + * @param n The number of bytes to append from the start of the output + * sequence to the end of the input sequence. The remainder of the output + * sequence is discarded. + * + * Requires a preceding call prepare(x) where x >= n, and + * no intervening operations that modify the input or output sequence. + * + * @note If @c n is greater than the size of the output sequence, the entire + * output sequence is moved to the input sequence and no error is issued. + */ + void commit(std::size_t n) + { + size_ += (std::min)(n, vector_.size() - size_); + vector_.resize(size_); + } + + /// Remove characters from the input sequence. + /** + * Removes @c n characters from the beginning of the input sequence. + * + * @note If @c n is greater than the size of the input sequence, the entire + * input sequence is consumed and no error is issued. + */ + void consume(std::size_t n) + { + std::size_t consume_length = (std::min)(n, size_); + vector_.erase(vector_.begin(), vector_.begin() + consume_length); + size_ -= consume_length; + } + +private: + std::vector& vector_; + std::size_t size_; + const std::size_t max_size_; +}; + +/** @defgroup dynamic_buffer asio::dynamic_buffer + * + * @brief The asio::dynamic_buffer function is used to create a + * dynamically resized buffer from a @c std::basic_string or @c std::vector. + */ +/*@{*/ + +/// Create a new dynamic buffer that represents the given string. +/** + * @returns dynamic_string_buffer(data). + */ +template +inline dynamic_string_buffer dynamic_buffer( + std::basic_string& data) ASIO_NOEXCEPT +{ + return dynamic_string_buffer(data); +} + +/// Create a new dynamic buffer that represents the given string. +/** + * @returns dynamic_string_buffer(data, + * max_size). + */ +template +inline dynamic_string_buffer dynamic_buffer( + std::basic_string& data, + std::size_t max_size) ASIO_NOEXCEPT +{ + return dynamic_string_buffer(data, max_size); +} + +/// Create a new dynamic buffer that represents the given vector. +/** + * @returns dynamic_vector_buffer(data). + */ +template +inline dynamic_vector_buffer dynamic_buffer( + std::vector& data) ASIO_NOEXCEPT +{ + return dynamic_vector_buffer(data); +} + +/// Create a new dynamic buffer that represents the given vector. +/** + * @returns dynamic_vector_buffer(data, max_size). + */ +template +inline dynamic_vector_buffer dynamic_buffer( + std::vector& data, + std::size_t max_size) ASIO_NOEXCEPT +{ + return dynamic_vector_buffer(data, max_size); +} + +/*@}*/ + +/** @defgroup buffer_copy asio::buffer_copy + * + * @brief The asio::buffer_copy function is used to copy bytes from a + * source buffer (or buffer sequence) to a target buffer (or buffer sequence). + * + * The @c buffer_copy function is available in two forms: + * + * @li A 2-argument form: @c buffer_copy(target, source) + * + * @li A 3-argument form: @c buffer_copy(target, source, max_bytes_to_copy) + * + * Both forms return the number of bytes actually copied. The number of bytes + * copied is the lesser of: + * + * @li @c buffer_size(target) + * + * @li @c buffer_size(source) + * + * @li @c If specified, @c max_bytes_to_copy. + * + * This prevents buffer overflow, regardless of the buffer sizes used in the + * copy operation. + * + * Note that @ref buffer_copy is implemented in terms of @c memcpy, and + * consequently it cannot be used to copy between overlapping memory regions. + */ +/*@{*/ + +namespace detail { + +inline std::size_t buffer_copy_1(const mutable_buffer& target, + const const_buffer& source) +{ + using namespace std; // For memcpy. + std::size_t target_size = target.size(); + std::size_t source_size = source.size(); + std::size_t n = target_size < source_size ? target_size : source_size; + if (n > 0) + memcpy(target.data(), source.data(), n); + return n; +} + +template +inline std::size_t buffer_copy(one_buffer, one_buffer, + TargetIterator target_begin, TargetIterator, + SourceIterator source_begin, SourceIterator) ASIO_NOEXCEPT +{ + return (buffer_copy_1)(*target_begin, *source_begin); +} + +template +inline std::size_t buffer_copy(one_buffer, one_buffer, + TargetIterator target_begin, TargetIterator, + SourceIterator source_begin, SourceIterator, + std::size_t max_bytes_to_copy) ASIO_NOEXCEPT +{ + return (buffer_copy_1)(*target_begin, + asio::buffer(*source_begin, max_bytes_to_copy)); +} + +template +std::size_t buffer_copy(one_buffer, multiple_buffers, + TargetIterator target_begin, TargetIterator, + SourceIterator source_begin, SourceIterator source_end, + std::size_t max_bytes_to_copy + = (std::numeric_limits::max)()) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + SourceIterator source_iter = source_begin; + + for (mutable_buffer target_buffer( + asio::buffer(*target_begin, max_bytes_to_copy)); + target_buffer.size() && source_iter != source_end; ++source_iter) + { + const_buffer source_buffer(*source_iter); + std::size_t bytes_copied = (buffer_copy_1)(target_buffer, source_buffer); + total_bytes_copied += bytes_copied; + target_buffer += bytes_copied; + } + + return total_bytes_copied; +} + +template +std::size_t buffer_copy(multiple_buffers, one_buffer, + TargetIterator target_begin, TargetIterator target_end, + SourceIterator source_begin, SourceIterator, + std::size_t max_bytes_to_copy + = (std::numeric_limits::max)()) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + TargetIterator target_iter = target_begin; + + for (const_buffer source_buffer( + asio::buffer(*source_begin, max_bytes_to_copy)); + source_buffer.size() && target_iter != target_end; ++target_iter) + { + mutable_buffer target_buffer(*target_iter); + std::size_t bytes_copied = (buffer_copy_1)(target_buffer, source_buffer); + total_bytes_copied += bytes_copied; + source_buffer += bytes_copied; + } + + return total_bytes_copied; +} + +template +std::size_t buffer_copy(multiple_buffers, multiple_buffers, + TargetIterator target_begin, TargetIterator target_end, + SourceIterator source_begin, SourceIterator source_end) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + + TargetIterator target_iter = target_begin; + std::size_t target_buffer_offset = 0; + + SourceIterator source_iter = source_begin; + std::size_t source_buffer_offset = 0; + + while (target_iter != target_end && source_iter != source_end) + { + mutable_buffer target_buffer = + mutable_buffer(*target_iter) + target_buffer_offset; + + const_buffer source_buffer = + const_buffer(*source_iter) + source_buffer_offset; + + std::size_t bytes_copied = (buffer_copy_1)(target_buffer, source_buffer); + total_bytes_copied += bytes_copied; + + if (bytes_copied == target_buffer.size()) + { + ++target_iter; + target_buffer_offset = 0; + } + else + target_buffer_offset += bytes_copied; + + if (bytes_copied == source_buffer.size()) + { + ++source_iter; + source_buffer_offset = 0; + } + else + source_buffer_offset += bytes_copied; + } + + return total_bytes_copied; +} + +template +std::size_t buffer_copy(multiple_buffers, multiple_buffers, + TargetIterator target_begin, TargetIterator target_end, + SourceIterator source_begin, SourceIterator source_end, + std::size_t max_bytes_to_copy) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + + TargetIterator target_iter = target_begin; + std::size_t target_buffer_offset = 0; + + SourceIterator source_iter = source_begin; + std::size_t source_buffer_offset = 0; + + while (total_bytes_copied != max_bytes_to_copy + && target_iter != target_end && source_iter != source_end) + { + mutable_buffer target_buffer = + mutable_buffer(*target_iter) + target_buffer_offset; + + const_buffer source_buffer = + const_buffer(*source_iter) + source_buffer_offset; + + std::size_t bytes_copied = (buffer_copy_1)( + target_buffer, asio::buffer(source_buffer, + max_bytes_to_copy - total_bytes_copied)); + total_bytes_copied += bytes_copied; + + if (bytes_copied == target_buffer.size()) + { + ++target_iter; + target_buffer_offset = 0; + } + else + target_buffer_offset += bytes_copied; + + if (bytes_copied == source_buffer.size()) + { + ++source_iter; + source_buffer_offset = 0; + } + else + source_buffer_offset += bytes_copied; + } + + return total_bytes_copied; +} + +} // namespace detail + +/// Copies bytes from a source buffer sequence to a target buffer sequence. +/** + * @param target A modifiable buffer sequence representing the memory regions to + * which the bytes will be copied. + * + * @param source A non-modifiable buffer sequence representing the memory + * regions from which the bytes will be copied. + * + * @returns The number of bytes copied. + * + * @note The number of bytes copied is the lesser of: + * + * @li @c buffer_size(target) + * + * @li @c buffer_size(source) + * + * This function is implemented in terms of @c memcpy, and consequently it + * cannot be used to copy between overlapping memory regions. + */ +template +inline std::size_t buffer_copy(const MutableBufferSequence& target, + const ConstBufferSequence& source) ASIO_NOEXCEPT +{ + return detail::buffer_copy( + detail::buffer_sequence_cardinality(), + detail::buffer_sequence_cardinality(), + asio::buffer_sequence_begin(target), + asio::buffer_sequence_end(target), + asio::buffer_sequence_begin(source), + asio::buffer_sequence_end(source)); +} + +/// Copies a limited number of bytes from a source buffer sequence to a target +/// buffer sequence. +/** + * @param target A modifiable buffer sequence representing the memory regions to + * which the bytes will be copied. + * + * @param source A non-modifiable buffer sequence representing the memory + * regions from which the bytes will be copied. + * + * @param max_bytes_to_copy The maximum number of bytes to be copied. + * + * @returns The number of bytes copied. + * + * @note The number of bytes copied is the lesser of: + * + * @li @c buffer_size(target) + * + * @li @c buffer_size(source) + * + * @li @c max_bytes_to_copy + * + * This function is implemented in terms of @c memcpy, and consequently it + * cannot be used to copy between overlapping memory regions. + */ +template +inline std::size_t buffer_copy(const MutableBufferSequence& target, + const ConstBufferSequence& source, + std::size_t max_bytes_to_copy) ASIO_NOEXCEPT +{ + return detail::buffer_copy( + detail::buffer_sequence_cardinality(), + detail::buffer_sequence_cardinality(), + asio::buffer_sequence_begin(target), + asio::buffer_sequence_end(target), + asio::buffer_sequence_begin(source), + asio::buffer_sequence_end(source), max_bytes_to_copy); +} + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" +#include "asio/detail/is_buffer_sequence.hpp" +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Trait to determine whether a type satisfies the MutableBufferSequence +/// requirements. +template +struct is_mutable_buffer_sequence +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_buffer_sequence +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +/// Trait to determine whether a type satisfies the ConstBufferSequence +/// requirements. +template +struct is_const_buffer_sequence +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_buffer_sequence +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +/// Trait to determine whether a type satisfies the DynamicBuffer requirements. +template +struct is_dynamic_buffer +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_dynamic_buffer +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFER_HPP diff --git a/subprojects/asio/include/asio/buffered_read_stream.hpp b/subprojects/asio/include/asio/buffered_read_stream.hpp new file mode 100644 index 000000000..c3e7f0b57 --- /dev/null +++ b/subprojects/asio/include/asio/buffered_read_stream.hpp @@ -0,0 +1,257 @@ +// +// buffered_read_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_READ_STREAM_HPP +#define ASIO_BUFFERED_READ_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_resize_guard.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Adds buffering to the read-related operations of a stream. +/** + * The buffered_read_stream class template can be used to add buffering to the + * synchronous and asynchronous read operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_read_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the executor associated with the object. + typedef typename lowest_layer_type::executor_type executor_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_read_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_read_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get a const reference to the lowest layer. + const lowest_layer_type& lowest_layer() const + { + return next_layer_.lowest_layer(); + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return next_layer_.lowest_layer().get_executor(); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_context() + { + return next_layer_.get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_service() + { + return next_layer_.get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + next_layer_.close(ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + return next_layer_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.write_some(buffers, ec); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + return next_layer_.async_write_some(buffers, + ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill(); + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred. + std::size_t fill(asio::error_code& ec); + + /// Start an asynchronous fill. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_fill(ASIO_MOVE_ARG(ReadHandler) handler); + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers); + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec); + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler); + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers); + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec); + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return storage_.size(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + ec = asio::error_code(); + return storage_.size(); + } + +private: + /// Copy data out of the internal buffer to the specified target buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const MutableBufferSequence& buffers) + { + std::size_t bytes_copied = asio::buffer_copy( + buffers, storage_.data(), storage_.size()); + storage_.consume(bytes_copied); + return bytes_copied; + } + + /// Copy data from the internal buffer to the specified target buffer, without + /// removing the data from the internal buffer. Returns the number of bytes + /// copied. + template + std::size_t peek_copy(const MutableBufferSequence& buffers) + { + return asio::buffer_copy(buffers, storage_.data(), storage_.size()); + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/buffered_read_stream.hpp" + +#endif // ASIO_BUFFERED_READ_STREAM_HPP diff --git a/subprojects/asio/include/asio/buffered_read_stream_fwd.hpp b/subprojects/asio/include/asio/buffered_read_stream_fwd.hpp new file mode 100644 index 000000000..334b88a5c --- /dev/null +++ b/subprojects/asio/include/asio/buffered_read_stream_fwd.hpp @@ -0,0 +1,25 @@ +// +// buffered_read_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP +#define ASIO_BUFFERED_READ_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace asio { + +template +class buffered_read_stream; + +} // namespace asio + +#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP diff --git a/subprojects/asio/include/asio/buffered_stream.hpp b/subprojects/asio/include/asio/buffered_stream.hpp new file mode 100644 index 000000000..8014fa4dd --- /dev/null +++ b/subprojects/asio/include/asio/buffered_stream.hpp @@ -0,0 +1,278 @@ +// +// buffered_stream.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_STREAM_HPP +#define ASIO_BUFFERED_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Adds buffering to the read- and write-related operations of a stream. +/** + * The buffered_stream class template can be used to add buffering to the + * synchronous and asynchronous read and write operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the executor associated with the object. + typedef typename lowest_layer_type::executor_type executor_type; + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a) + : inner_stream_impl_(a), + stream_impl_(inner_stream_impl_) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a, std::size_t read_buffer_size, + std::size_t write_buffer_size) + : inner_stream_impl_(a, write_buffer_size), + stream_impl_(inner_stream_impl_, read_buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return stream_impl_.next_layer().next_layer(); + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return stream_impl_.lowest_layer(); + } + + /// Get a const reference to the lowest layer. + const lowest_layer_type& lowest_layer() const + { + return stream_impl_.lowest_layer(); + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return stream_impl_.lowest_layer().get_executor(); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_context() + { + return stream_impl_.get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_service() + { + return stream_impl_.get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Close the stream. + void close() + { + stream_impl_.close(); + } + + /// Close the stream. + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + stream_impl_.close(ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + return stream_impl_.next_layer().flush(); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred. + std::size_t flush(asio::error_code& ec) + { + return stream_impl_.next_layer().flush(ec); + } + + /// Start an asynchronous flush. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_flush(ASIO_MOVE_ARG(WriteHandler) handler) + { + return stream_impl_.next_layer().async_flush( + ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + return stream_impl_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.write_some(buffers, ec); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + return stream_impl_.async_write_some(buffers, + ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + return stream_impl_.fill(); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred. + std::size_t fill(asio::error_code& ec) + { + return stream_impl_.fill(ec); + } + + /// Start an asynchronous fill. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_fill(ASIO_MOVE_ARG(ReadHandler) handler) + { + return stream_impl_.async_fill(ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + return stream_impl_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.read_some(buffers, ec); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + return stream_impl_.async_read_some(buffers, + ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) + { + return stream_impl_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.peek(buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return stream_impl_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + return stream_impl_.in_avail(ec); + } + +private: + // The buffered write stream. + typedef buffered_write_stream write_stream_type; + write_stream_type inner_stream_impl_; + + // The buffered read stream. + typedef buffered_read_stream read_stream_type; + read_stream_type stream_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_STREAM_HPP diff --git a/subprojects/asio/include/asio/buffered_stream_fwd.hpp b/subprojects/asio/include/asio/buffered_stream_fwd.hpp new file mode 100644 index 000000000..3492979d2 --- /dev/null +++ b/subprojects/asio/include/asio/buffered_stream_fwd.hpp @@ -0,0 +1,25 @@ +// +// buffered_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_STREAM_FWD_HPP +#define ASIO_BUFFERED_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace asio { + +template +class buffered_stream; + +} // namespace asio + +#endif // ASIO_BUFFERED_STREAM_FWD_HPP diff --git a/subprojects/asio/include/asio/buffered_write_stream.hpp b/subprojects/asio/include/asio/buffered_write_stream.hpp new file mode 100644 index 000000000..aac33d38f --- /dev/null +++ b/subprojects/asio/include/asio/buffered_write_stream.hpp @@ -0,0 +1,249 @@ +// +// buffered_write_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP +#define ASIO_BUFFERED_WRITE_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/write.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Adds buffering to the write-related operations of a stream. +/** + * The buffered_write_stream class template can be used to add buffering to the + * synchronous and asynchronous write operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_write_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the executor associated with the object. + typedef typename lowest_layer_type::executor_type executor_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_write_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_write_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get a const reference to the lowest layer. + const lowest_layer_type& lowest_layer() const + { + return next_layer_.lowest_layer(); + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return next_layer_.lowest_layer().get_executor(); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_context() + { + return next_layer_.get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_service() + { + return next_layer_.get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + next_layer_.close(ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush(); + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred. + std::size_t flush(asio::error_code& ec); + + /// Start an asynchronous flush. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_flush(ASIO_MOVE_ARG(WriteHandler) handler); + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers); + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec); + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler); + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + return next_layer_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.read_some(buffers, ec); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + return next_layer_.async_read_some(buffers, + ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) + { + return next_layer_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.peek(buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return next_layer_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + return next_layer_.in_avail(ec); + } + +private: + /// Copy data into the internal buffer from the specified source buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const ConstBufferSequence& buffers); + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/buffered_write_stream.hpp" + +#endif // ASIO_BUFFERED_WRITE_STREAM_HPP diff --git a/subprojects/asio/include/asio/buffered_write_stream_fwd.hpp b/subprojects/asio/include/asio/buffered_write_stream_fwd.hpp new file mode 100644 index 000000000..6ef54ba0d --- /dev/null +++ b/subprojects/asio/include/asio/buffered_write_stream_fwd.hpp @@ -0,0 +1,25 @@ +// +// buffered_write_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP +#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace asio { + +template +class buffered_write_stream; + +} // namespace asio + +#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP diff --git a/subprojects/asio/include/asio/buffers_iterator.hpp b/subprojects/asio/include/asio/buffers_iterator.hpp new file mode 100644 index 000000000..f5c9d4c35 --- /dev/null +++ b/subprojects/asio/include/asio/buffers_iterator.hpp @@ -0,0 +1,521 @@ +// +// buffers_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERS_ITERATOR_HPP +#define ASIO_BUFFERS_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/buffer.hpp" +#include "asio/detail/assert.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + template + struct buffers_iterator_types_helper; + + template <> + struct buffers_iterator_types_helper + { + typedef const_buffer buffer_type; + template + struct byte_type + { + typedef typename add_const::type type; + }; + }; + + template <> + struct buffers_iterator_types_helper + { + typedef mutable_buffer buffer_type; + template + struct byte_type + { + typedef ByteType type; + }; + }; + + template + struct buffers_iterator_types + { + enum + { + is_mutable = is_convertible< + typename BufferSequence::value_type, + mutable_buffer>::value + }; + typedef buffers_iterator_types_helper helper; + typedef typename helper::buffer_type buffer_type; + typedef typename helper::template byte_type::type byte_type; + typedef typename BufferSequence::const_iterator const_iterator; + }; + + template + struct buffers_iterator_types + { + typedef mutable_buffer buffer_type; + typedef ByteType byte_type; + typedef const mutable_buffer* const_iterator; + }; + + template + struct buffers_iterator_types + { + typedef const_buffer buffer_type; + typedef typename add_const::type byte_type; + typedef const const_buffer* const_iterator; + }; + +#if !defined(ASIO_NO_DEPRECATED) + + template + struct buffers_iterator_types + { + typedef mutable_buffer buffer_type; + typedef ByteType byte_type; + typedef const mutable_buffer* const_iterator; + }; + + template + struct buffers_iterator_types + { + typedef const_buffer buffer_type; + typedef typename add_const::type byte_type; + typedef const const_buffer* const_iterator; + }; + +#endif // !defined(ASIO_NO_DEPRECATED) +} + +/// A random access iterator over the bytes in a buffer sequence. +template +class buffers_iterator +{ +private: + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::buffer_type buffer_type; + + typedef typename detail::buffers_iterator_types::const_iterator buffer_sequence_iterator_type; + +public: + /// The type used for the distance between two iterators. + typedef std::ptrdiff_t difference_type; + + /// The type of the value pointed to by the iterator. + typedef ByteType value_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The type of the result of applying operator->() to the iterator. + /** + * If the buffer sequence stores buffer objects that are convertible to + * mutable_buffer, this is a pointer to a non-const ByteType. Otherwise, a + * pointer to a const ByteType. + */ + typedef const_or_non_const_ByteType* pointer; +#else // defined(GENERATING_DOCUMENTATION) + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::byte_type* pointer; +#endif // defined(GENERATING_DOCUMENTATION) + +#if defined(GENERATING_DOCUMENTATION) + /// The type of the result of applying operator*() to the iterator. + /** + * If the buffer sequence stores buffer objects that are convertible to + * mutable_buffer, this is a reference to a non-const ByteType. Otherwise, a + * reference to a const ByteType. + */ + typedef const_or_non_const_ByteType& reference; +#else // defined(GENERATING_DOCUMENTATION) + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::byte_type& reference; +#endif // defined(GENERATING_DOCUMENTATION) + + /// The iterator category. + typedef std::random_access_iterator_tag iterator_category; + + /// Default constructor. Creates an iterator in an undefined state. + buffers_iterator() + : current_buffer_(), + current_buffer_position_(0), + begin_(), + current_(), + end_(), + position_(0) + { + } + + /// Construct an iterator representing the beginning of the buffers' data. + static buffers_iterator begin(const BufferSequence& buffers) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + __attribute__ ((__noinline__)) +#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + { + buffers_iterator new_iter; + new_iter.begin_ = asio::buffer_sequence_begin(buffers); + new_iter.current_ = asio::buffer_sequence_begin(buffers); + new_iter.end_ = asio::buffer_sequence_end(buffers); + while (new_iter.current_ != new_iter.end_) + { + new_iter.current_buffer_ = *new_iter.current_; + if (new_iter.current_buffer_.size() > 0) + break; + ++new_iter.current_; + } + return new_iter; + } + + /// Construct an iterator representing the end of the buffers' data. + static buffers_iterator end(const BufferSequence& buffers) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + __attribute__ ((__noinline__)) +#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + { + buffers_iterator new_iter; + new_iter.begin_ = asio::buffer_sequence_begin(buffers); + new_iter.current_ = asio::buffer_sequence_begin(buffers); + new_iter.end_ = asio::buffer_sequence_end(buffers); + while (new_iter.current_ != new_iter.end_) + { + buffer_type buffer = *new_iter.current_; + new_iter.position_ += buffer.size(); + ++new_iter.current_; + } + return new_iter; + } + + /// Dereference an iterator. + reference operator*() const + { + return dereference(); + } + + /// Dereference an iterator. + pointer operator->() const + { + return &dereference(); + } + + /// Access an individual element. + reference operator[](std::ptrdiff_t difference) const + { + buffers_iterator tmp(*this); + tmp.advance(difference); + return *tmp; + } + + /// Increment operator (prefix). + buffers_iterator& operator++() + { + increment(); + return *this; + } + + /// Increment operator (postfix). + buffers_iterator operator++(int) + { + buffers_iterator tmp(*this); + ++*this; + return tmp; + } + + /// Decrement operator (prefix). + buffers_iterator& operator--() + { + decrement(); + return *this; + } + + /// Decrement operator (postfix). + buffers_iterator operator--(int) + { + buffers_iterator tmp(*this); + --*this; + return tmp; + } + + /// Addition operator. + buffers_iterator& operator+=(std::ptrdiff_t difference) + { + advance(difference); + return *this; + } + + /// Subtraction operator. + buffers_iterator& operator-=(std::ptrdiff_t difference) + { + advance(-difference); + return *this; + } + + /// Addition operator. + friend buffers_iterator operator+(const buffers_iterator& iter, + std::ptrdiff_t difference) + { + buffers_iterator tmp(iter); + tmp.advance(difference); + return tmp; + } + + /// Addition operator. + friend buffers_iterator operator+(std::ptrdiff_t difference, + const buffers_iterator& iter) + { + buffers_iterator tmp(iter); + tmp.advance(difference); + return tmp; + } + + /// Subtraction operator. + friend buffers_iterator operator-(const buffers_iterator& iter, + std::ptrdiff_t difference) + { + buffers_iterator tmp(iter); + tmp.advance(-difference); + return tmp; + } + + /// Subtraction operator. + friend std::ptrdiff_t operator-(const buffers_iterator& a, + const buffers_iterator& b) + { + return b.distance_to(a); + } + + /// Test two iterators for equality. + friend bool operator==(const buffers_iterator& a, const buffers_iterator& b) + { + return a.equal(b); + } + + /// Test two iterators for inequality. + friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b) + { + return !a.equal(b); + } + + /// Compare two iterators. + friend bool operator<(const buffers_iterator& a, const buffers_iterator& b) + { + return a.distance_to(b) > 0; + } + + /// Compare two iterators. + friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b) + { + return !(b < a); + } + + /// Compare two iterators. + friend bool operator>(const buffers_iterator& a, const buffers_iterator& b) + { + return b < a; + } + + /// Compare two iterators. + friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b) + { + return !(a < b); + } + +private: + // Dereference the iterator. + reference dereference() const + { + return static_cast( + current_buffer_.data())[current_buffer_position_]; + } + + // Compare two iterators for equality. + bool equal(const buffers_iterator& other) const + { + return position_ == other.position_; + } + + // Increment the iterator. + void increment() + { + ASIO_ASSERT(current_ != end_ && "iterator out of bounds"); + ++position_; + + // Check if the increment can be satisfied by the current buffer. + ++current_buffer_position_; + if (current_buffer_position_ != current_buffer_.size()) + return; + + // Find the next non-empty buffer. + ++current_; + current_buffer_position_ = 0; + while (current_ != end_) + { + current_buffer_ = *current_; + if (current_buffer_.size() > 0) + return; + ++current_; + } + } + + // Decrement the iterator. + void decrement() + { + ASIO_ASSERT(position_ > 0 && "iterator out of bounds"); + --position_; + + // Check if the decrement can be satisfied by the current buffer. + if (current_buffer_position_ != 0) + { + --current_buffer_position_; + return; + } + + // Find the previous non-empty buffer. + buffer_sequence_iterator_type iter = current_; + while (iter != begin_) + { + --iter; + buffer_type buffer = *iter; + std::size_t buffer_size = buffer.size(); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size - 1; + return; + } + } + } + + // Advance the iterator by the specified distance. + void advance(std::ptrdiff_t n) + { + if (n > 0) + { + ASIO_ASSERT(current_ != end_ && "iterator out of bounds"); + for (;;) + { + std::ptrdiff_t current_buffer_balance + = current_buffer_.size() - current_buffer_position_; + + // Check if the advance can be satisfied by the current buffer. + if (current_buffer_balance > n) + { + position_ += n; + current_buffer_position_ += n; + return; + } + + // Update position. + n -= current_buffer_balance; + position_ += current_buffer_balance; + + // Move to next buffer. If it is empty then it will be skipped on the + // next iteration of this loop. + if (++current_ == end_) + { + ASIO_ASSERT(n == 0 && "iterator out of bounds"); + current_buffer_ = buffer_type(); + current_buffer_position_ = 0; + return; + } + current_buffer_ = *current_; + current_buffer_position_ = 0; + } + } + else if (n < 0) + { + std::size_t abs_n = -n; + ASIO_ASSERT(position_ >= abs_n && "iterator out of bounds"); + for (;;) + { + // Check if the advance can be satisfied by the current buffer. + if (current_buffer_position_ >= abs_n) + { + position_ -= abs_n; + current_buffer_position_ -= abs_n; + return; + } + + // Update position. + abs_n -= current_buffer_position_; + position_ -= current_buffer_position_; + + // Check if we've reached the beginning of the buffers. + if (current_ == begin_) + { + ASIO_ASSERT(abs_n == 0 && "iterator out of bounds"); + current_buffer_position_ = 0; + return; + } + + // Find the previous non-empty buffer. + buffer_sequence_iterator_type iter = current_; + while (iter != begin_) + { + --iter; + buffer_type buffer = *iter; + std::size_t buffer_size = buffer.size(); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size; + break; + } + } + } + } + } + + // Determine the distance between two iterators. + std::ptrdiff_t distance_to(const buffers_iterator& other) const + { + return other.position_ - position_; + } + + buffer_type current_buffer_; + std::size_t current_buffer_position_; + buffer_sequence_iterator_type begin_; + buffer_sequence_iterator_type current_; + buffer_sequence_iterator_type end_; + std::size_t position_; +}; + +/// Construct an iterator representing the beginning of the buffers' data. +template +inline buffers_iterator buffers_begin( + const BufferSequence& buffers) +{ + return buffers_iterator::begin(buffers); +} + +/// Construct an iterator representing the end of the buffers' data. +template +inline buffers_iterator buffers_end( + const BufferSequence& buffers) +{ + return buffers_iterator::end(buffers); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERS_ITERATOR_HPP diff --git a/subprojects/asio/include/asio/completion_condition.hpp b/subprojects/asio/include/asio/completion_condition.hpp new file mode 100644 index 000000000..563f417d1 --- /dev/null +++ b/subprojects/asio/include/asio/completion_condition.hpp @@ -0,0 +1,218 @@ +// +// completion_condition.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_COMPLETION_CONDITION_HPP +#define ASIO_COMPLETION_CONDITION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail { + +// The default maximum number of bytes to transfer in a single operation. +enum default_max_transfer_size_t { default_max_transfer_size = 65536 }; + +// Adapt result of old-style completion conditions (which had a bool result +// where true indicated that the operation was complete). +inline std::size_t adapt_completion_condition_result(bool result) +{ + return result ? 0 : default_max_transfer_size; +} + +// Adapt result of current completion conditions (which have a size_t result +// where 0 means the operation is complete, and otherwise the result is the +// maximum number of bytes to transfer on the next underlying operation). +inline std::size_t adapt_completion_condition_result(std::size_t result) +{ + return result; +} + +class transfer_all_t +{ +public: + typedef std::size_t result_type; + + template + std::size_t operator()(const Error& err, std::size_t) + { + return !!err ? 0 : default_max_transfer_size; + } +}; + +class transfer_at_least_t +{ +public: + typedef std::size_t result_type; + + explicit transfer_at_least_t(std::size_t minimum) + : minimum_(minimum) + { + } + + template + std::size_t operator()(const Error& err, std::size_t bytes_transferred) + { + return (!!err || bytes_transferred >= minimum_) + ? 0 : default_max_transfer_size; + } + +private: + std::size_t minimum_; +}; + +class transfer_exactly_t +{ +public: + typedef std::size_t result_type; + + explicit transfer_exactly_t(std::size_t size) + : size_(size) + { + } + + template + std::size_t operator()(const Error& err, std::size_t bytes_transferred) + { + return (!!err || bytes_transferred >= size_) ? 0 : + (size_ - bytes_transferred < default_max_transfer_size + ? size_ - bytes_transferred : std::size_t(default_max_transfer_size)); + } + +private: + std::size_t size_; +}; + +} // namespace detail + +/** + * @defgroup completion_condition Completion Condition Function Objects + * + * Function objects used for determining when a read or write operation should + * complete. + */ +/*@{*/ + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until all of the data has been transferred, +/// or until an error occurs. +/** + * This function is used to create an object, of unspecified type, that meets + * CompletionCondition requirements. + * + * @par Example + * Reading until a buffer is full: + * @code + * boost::array buf; + * asio::error_code ec; + * std::size_t n = asio::read( + * sock, asio::buffer(buf), + * asio::transfer_all(), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * // n == 128 + * } + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_all(); +#else +inline detail::transfer_all_t transfer_all() +{ + return detail::transfer_all_t(); +} +#endif + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until a minimum number of bytes has been +/// transferred, or until an error occurs. +/** + * This function is used to create an object, of unspecified type, that meets + * CompletionCondition requirements. + * + * @par Example + * Reading until a buffer is full or contains at least 64 bytes: + * @code + * boost::array buf; + * asio::error_code ec; + * std::size_t n = asio::read( + * sock, asio::buffer(buf), + * asio::transfer_at_least(64), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * // n >= 64 && n <= 128 + * } + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_at_least(std::size_t minimum); +#else +inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum) +{ + return detail::transfer_at_least_t(minimum); +} +#endif + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until an exact number of bytes has been +/// transferred, or until an error occurs. +/** + * This function is used to create an object, of unspecified type, that meets + * CompletionCondition requirements. + * + * @par Example + * Reading until a buffer is full or contains exactly 64 bytes: + * @code + * boost::array buf; + * asio::error_code ec; + * std::size_t n = asio::read( + * sock, asio::buffer(buf), + * asio::transfer_exactly(64), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * // n == 64 + * } + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_exactly(std::size_t size); +#else +inline detail::transfer_exactly_t transfer_exactly(std::size_t size) +{ + return detail::transfer_exactly_t(size); +} +#endif + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_COMPLETION_CONDITION_HPP diff --git a/subprojects/asio/include/asio/connect.hpp b/subprojects/asio/include/asio/connect.hpp new file mode 100644 index 000000000..89a9f35f9 --- /dev/null +++ b/subprojects/asio/include/asio/connect.hpp @@ -0,0 +1,1060 @@ +// +// connect.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_CONNECT_HPP +#define ASIO_CONNECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/basic_socket.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + char (&has_iterator_helper(...))[2]; + + template + char has_iterator_helper(T*, typename T::iterator* = 0); + + template + struct has_iterator_typedef + { + enum { value = (sizeof((has_iterator_helper)((T*)(0))) == 1) }; + }; +} // namespace detail + +/// Type trait used to determine whether a type is an endpoint sequence that can +/// be used with with @c connect and @c async_connect. +template +struct is_endpoint_sequence +{ +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true if the type may be used as an endpoint sequence. + static const bool value; +#else + enum + { + value = detail::has_iterator_typedef::value + }; +#endif +}; + +/** + * @defgroup connect asio::connect + * + * @brief The @c connect function is a composed operation that establishes a + * socket connection by trying each endpoint in a sequence. + */ +/*@{*/ + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @returns The successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(io_context); + * asio::connect(s, r.resolve(q)); @endcode + */ +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, + typename enable_if::value>::type* = 0); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, the successfully connected endpoint. Otherwise, a + * default-constructed endpoint. + * + * @par Example + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(io_context); + * asio::error_code ec; + * asio::connect(s, r.resolve(q), ec); + * if (ec) + * { + * // An error occurred. + * } @endcode + */ +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, asio::error_code& ec, + typename enable_if::value>::type* = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, Iterator begin, + typename enable_if::value>::type* = 0); + +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, + Iterator begin, asio::error_code& ec, + typename enable_if::value>::type* = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @returns An iterator denoting the successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(io_context); + * asio::connect(s, e.begin(), e.end()); @endcode + */ +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @par Example + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(io_context); + * asio::error_code ec; + * asio::connect(s, e.begin(), e.end(), ec); + * if (ec) + * { + * // An error occurred. + * } @endcode + */ +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end, asio::error_code& ec); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @returns The successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(io_context); + * tcp::endpoint e = asio::connect(s, + * r.resolve(q), my_connect_condition()); + * std::cout << "Connected to: " << e << std::endl; @endcode + */ +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + typename enable_if::value>::type* = 0); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, the successfully connected endpoint. Otherwise, a + * default-constructed endpoint. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(io_context); + * asio::error_code ec; + * tcp::endpoint e = asio::connect(s, + * r.resolve(q), my_connect_condition(), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << e << std::endl; + * } @endcode + */ +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + asio::error_code& ec, + typename enable_if::value>::type* = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, + Iterator begin, ConnectCondition connect_condition, + typename enable_if::value>::type* = 0); + +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, Iterator begin, + ConnectCondition connect_condition, asio::error_code& ec, + typename enable_if::value>::type* = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @returns An iterator denoting the successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(io_context); + * tcp::resolver::results_type::iterator i = asio::connect( + * s, e.begin(), e.end(), my_connect_condition()); + * std::cout << "Connected to: " << i->endpoint() << std::endl; @endcode + */ +template +Iterator connect(basic_socket& s, Iterator begin, + Iterator end, ConnectCondition connect_condition); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(io_context); + * asio::error_code ec; + * tcp::resolver::results_type::iterator i = asio::connect( + * s, e.begin(), e.end(), my_connect_condition()); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << i->endpoint() << std::endl; + * } @endcode + */ +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end, ConnectCondition connect_condition, + asio::error_code& ec); + +/*@}*/ + +/** + * @defgroup async_connect asio::async_connect + * + * @brief The @c async_connect function is a composed asynchronous operation + * that establishes a socket connection by trying each endpoint in a sequence. + */ +/*@{*/ + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, the successfully connected endpoint. + * // Otherwise, a default-constructed endpoint. + * const typename Protocol::endpoint& endpoint + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(io_context); + * + * // ... + * + * r.async_resolve(q, resolve_handler); + * + * // ... + * + * void resolve_handler( + * const asio::error_code& ec, + * tcp::resolver::results_type results) + * { + * if (!ec) + * { + * asio::async_connect(s, results, connect_handler); + * } + * } + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * const tcp::endpoint& endpoint) + * { + * // ... + * } @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, + void (asio::error_code, typename Protocol::endpoint)) +async_connect(basic_socket& s, + const EndpointSequence& endpoints, + ASIO_MOVE_ARG(RangeConnectHandler) handler, + typename enable_if::value>::type* = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Asynchronously establishes a socket +/// connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, + Iterator begin, ASIO_MOVE_ARG(IteratorConnectHandler) handler, + typename enable_if::value>::type* = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code std::vector endpoints = ...; + * tcp::socket s(io_context); + * asio::async_connect(s, + * endpoints.begin(), endpoints.end(), + * connect_handler); + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * std::vector::iterator i) + * { + * // ... + * } @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, + Iterator begin, Iterator end, + ASIO_MOVE_ARG(IteratorConnectHandler) handler); + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(io_context); + * + * // ... + * + * r.async_resolve(q, resolve_handler); + * + * // ... + * + * void resolve_handler( + * const asio::error_code& ec, + * tcp::resolver::results_type results) + * { + * if (!ec) + * { + * asio::async_connect(s, results, + * my_connect_condition(), + * connect_handler); + * } + * } + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * const tcp::endpoint& endpoint) + * { + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << endpoint << std::endl; + * } + * } @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, + void (asio::error_code, typename Protocol::endpoint)) +async_connect(basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + ASIO_MOVE_ARG(RangeConnectHandler) handler, + typename enable_if::value>::type* = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Asynchronously establishes a socket +/// connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, Iterator begin, + ConnectCondition connect_condition, + ASIO_MOVE_ARG(IteratorConnectHandler) handler, + typename enable_if::value>::type* = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(io_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(io_context); + * + * // ... + * + * r.async_resolve(q, resolve_handler); + * + * // ... + * + * void resolve_handler( + * const asio::error_code& ec, + * tcp::resolver::iterator i) + * { + * if (!ec) + * { + * tcp::resolver::iterator end; + * asio::async_connect(s, i, end, + * my_connect_condition(), + * connect_handler); + * } + * } + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * tcp::resolver::iterator i) + * { + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << i->endpoint() << std::endl; + * } + * } @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, + Iterator begin, Iterator end, ConnectCondition connect_condition, + ASIO_MOVE_ARG(IteratorConnectHandler) handler); + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/connect.hpp" + +#endif diff --git a/subprojects/asio/include/asio/coroutine.hpp b/subprojects/asio/include/asio/coroutine.hpp new file mode 100644 index 000000000..cd2d99e53 --- /dev/null +++ b/subprojects/asio/include/asio/coroutine.hpp @@ -0,0 +1,328 @@ +// +// coroutine.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_COROUTINE_HPP +#define ASIO_COROUTINE_HPP + +namespace asio { +namespace detail { + +class coroutine_ref; + +} // namespace detail + +/// Provides support for implementing stackless coroutines. +/** + * The @c coroutine class may be used to implement stackless coroutines. The + * class itself is used to store the current state of the coroutine. + * + * Coroutines are copy-constructible and assignable, and the space overhead is + * a single int. They can be used as a base class: + * + * @code class session : coroutine + * { + * ... + * }; @endcode + * + * or as a data member: + * + * @code class session + * { + * ... + * coroutine coro_; + * }; @endcode + * + * or even bound in as a function argument using lambdas or @c bind(). The + * important thing is that as the application maintains a copy of the object + * for as long as the coroutine must be kept alive. + * + * @par Pseudo-keywords + * + * A coroutine is used in conjunction with certain "pseudo-keywords", which + * are implemented as macros. These macros are defined by a header file: + * + * @code #include @endcode + * + * and may conversely be undefined as follows: + * + * @code #include @endcode + * + * reenter + * + * The @c reenter macro is used to define the body of a coroutine. It takes a + * single argument: a pointer or reference to a coroutine object. For example, + * if the base class is a coroutine object you may write: + * + * @code reenter (this) + * { + * ... coroutine body ... + * } @endcode + * + * and if a data member or other variable you can write: + * + * @code reenter (coro_) + * { + * ... coroutine body ... + * } @endcode + * + * When @c reenter is executed at runtime, control jumps to the location of the + * last @c yield or @c fork. + * + * The coroutine body may also be a single statement, such as: + * + * @code reenter (this) for (;;) + * { + * ... + * } @endcode + * + * @b Limitation: The @c reenter macro is implemented using a switch. This + * means that you must take care when using local variables within the + * coroutine body. The local variable is not allowed in a position where + * reentering the coroutine could bypass the variable definition. + * + * yield statement + * + * This form of the @c yield keyword is often used with asynchronous operations: + * + * @code yield socket_->async_read_some(buffer(*buffer_), *this); @endcode + * + * This divides into four logical steps: + * + * @li @c yield saves the current state of the coroutine. + * @li The statement initiates the asynchronous operation. + * @li The resume point is defined immediately following the statement. + * @li Control is transferred to the end of the coroutine body. + * + * When the asynchronous operation completes, the function object is invoked + * and @c reenter causes control to transfer to the resume point. It is + * important to remember to carry the coroutine state forward with the + * asynchronous operation. In the above snippet, the current class is a + * function object object with a coroutine object as base class or data member. + * + * The statement may also be a compound statement, and this permits us to + * define local variables with limited scope: + * + * @code yield + * { + * mutable_buffers_1 b = buffer(*buffer_); + * socket_->async_read_some(b, *this); + * } @endcode + * + * yield return expression ; + * + * This form of @c yield is often used in generators or coroutine-based parsers. + * For example, the function object: + * + * @code struct interleave : coroutine + * { + * istream& is1; + * istream& is2; + * char operator()(char c) + * { + * reenter (this) for (;;) + * { + * yield return is1.get(); + * yield return is2.get(); + * } + * } + * }; @endcode + * + * defines a trivial coroutine that interleaves the characters from two input + * streams. + * + * This type of @c yield divides into three logical steps: + * + * @li @c yield saves the current state of the coroutine. + * @li The resume point is defined immediately following the semicolon. + * @li The value of the expression is returned from the function. + * + * yield ; + * + * This form of @c yield is equivalent to the following steps: + * + * @li @c yield saves the current state of the coroutine. + * @li The resume point is defined immediately following the semicolon. + * @li Control is transferred to the end of the coroutine body. + * + * This form might be applied when coroutines are used for cooperative + * threading and scheduling is explicitly managed. For example: + * + * @code struct task : coroutine + * { + * ... + * void operator()() + * { + * reenter (this) + * { + * while (... not finished ...) + * { + * ... do something ... + * yield; + * ... do some more ... + * yield; + * } + * } + * } + * ... + * }; + * ... + * task t1, t2; + * for (;;) + * { + * t1(); + * t2(); + * } @endcode + * + * yield break ; + * + * The final form of @c yield is used to explicitly terminate the coroutine. + * This form is comprised of two steps: + * + * @li @c yield sets the coroutine state to indicate termination. + * @li Control is transferred to the end of the coroutine body. + * + * Once terminated, calls to is_complete() return true and the coroutine cannot + * be reentered. + * + * Note that a coroutine may also be implicitly terminated if the coroutine + * body is exited without a yield, e.g. by return, throw or by running to the + * end of the body. + * + * fork statement + * + * The @c fork pseudo-keyword is used when "forking" a coroutine, i.e. splitting + * it into two (or more) copies. One use of @c fork is in a server, where a new + * coroutine is created to handle each client connection: + * + * @code reenter (this) + * { + * do + * { + * socket_.reset(new tcp::socket(io_context_)); + * yield acceptor->async_accept(*socket_, *this); + * fork server(*this)(); + * } while (is_parent()); + * ... client-specific handling follows ... + * } @endcode + * + * The logical steps involved in a @c fork are: + * + * @li @c fork saves the current state of the coroutine. + * @li The statement creates a copy of the coroutine and either executes it + * immediately or schedules it for later execution. + * @li The resume point is defined immediately following the semicolon. + * @li For the "parent", control immediately continues from the next line. + * + * The functions is_parent() and is_child() can be used to differentiate + * between parent and child. You would use these functions to alter subsequent + * control flow. + * + * Note that @c fork doesn't do the actual forking by itself. It is the + * application's responsibility to create a clone of the coroutine and call it. + * The clone can be called immediately, as above, or scheduled for delayed + * execution using something like io_context::post(). + * + * @par Alternate macro names + * + * If preferred, an application can use macro names that follow a more typical + * naming convention, rather than the pseudo-keywords. These are: + * + * @li @c ASIO_CORO_REENTER instead of @c reenter + * @li @c ASIO_CORO_YIELD instead of @c yield + * @li @c ASIO_CORO_FORK instead of @c fork + */ +class coroutine +{ +public: + /// Constructs a coroutine in its initial state. + coroutine() : value_(0) {} + + /// Returns true if the coroutine is the child of a fork. + bool is_child() const { return value_ < 0; } + + /// Returns true if the coroutine is the parent of a fork. + bool is_parent() const { return !is_child(); } + + /// Returns true if the coroutine has reached its terminal state. + bool is_complete() const { return value_ == -1; } + +private: + friend class detail::coroutine_ref; + int value_; +}; + + +namespace detail { + +class coroutine_ref +{ +public: + coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {} + coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {} + ~coroutine_ref() { if (!modified_) value_ = -1; } + operator int() const { return value_; } + int& operator=(int v) { modified_ = true; return value_ = v; } +private: + void operator=(const coroutine_ref&); + int& value_; + bool modified_; +}; + +} // namespace detail +} // namespace asio + +#define ASIO_CORO_REENTER(c) \ + switch (::asio::detail::coroutine_ref _coro_value = c) \ + case -1: if (_coro_value) \ + { \ + goto terminate_coroutine; \ + terminate_coroutine: \ + _coro_value = -1; \ + goto bail_out_of_coroutine; \ + bail_out_of_coroutine: \ + break; \ + } \ + else /* fall-through */ case 0: + +#define ASIO_CORO_YIELD_IMPL(n) \ + for (_coro_value = (n);;) \ + if (_coro_value == 0) \ + { \ + case (n): ; \ + break; \ + } \ + else \ + switch (_coro_value ? 0 : 1) \ + for (;;) \ + /* fall-through */ case -1: if (_coro_value) \ + goto terminate_coroutine; \ + else for (;;) \ + /* fall-through */ case 1: if (_coro_value) \ + goto bail_out_of_coroutine; \ + else /* fall-through */ case 0: + +#define ASIO_CORO_FORK_IMPL(n) \ + for (_coro_value = -(n);; _coro_value = (n)) \ + if (_coro_value == (n)) \ + { \ + case -(n): ; \ + break; \ + } \ + else + +#if defined(_MSC_VER) +# define ASIO_CORO_YIELD ASIO_CORO_YIELD_IMPL(__COUNTER__ + 1) +# define ASIO_CORO_FORK ASIO_CORO_FORK_IMPL(__COUNTER__ + 1) +#else // defined(_MSC_VER) +# define ASIO_CORO_YIELD ASIO_CORO_YIELD_IMPL(__LINE__) +# define ASIO_CORO_FORK ASIO_CORO_FORK_IMPL(__LINE__) +#endif // defined(_MSC_VER) + +#endif // ASIO_COROUTINE_HPP diff --git a/subprojects/asio/include/asio/datagram_socket_service.hpp b/subprojects/asio/include/asio/datagram_socket_service.hpp new file mode 100644 index 000000000..7dc1a3b34 --- /dev/null +++ b/subprojects/asio/include/asio/datagram_socket_service.hpp @@ -0,0 +1,466 @@ +// +// datagram_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DATAGRAM_SOCKET_SERVICE_HPP +#define ASIO_DATAGRAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a datagram socket. +template +class datagram_socket_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_WINDOWS_RUNTIME) + typedef detail::null_socket_service service_impl_type; +#elif defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#else + typedef detail::reactive_socket_service service_impl_type; +#endif + +public: + /// The type of a datagram socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new datagram socket service for the specified io_context. + explicit datagram_socket_service(asio::io_context& io_context) + : asio::detail::service_base< + datagram_socket_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new datagram socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new datagram socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another datagram socket implementation. + void move_assign(implementation_type& impl, + datagram_socket_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } + + // All socket services have access to each other's implementations. + template friend class datagram_socket_service; + + /// Move-construct a new datagram socket implementation from another protocol + /// type. + template + void converting_move_construct(implementation_type& impl, + datagram_socket_service& other_service, + typename datagram_socket_service< + Protocol1>::implementation_type& other_impl, + typename enable_if::value>::type* = 0) + { + service_impl_.template converting_move_construct( + impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a datagram socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + // Open a new datagram socket implementation. + ASIO_SYNC_OP_VOID open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (protocol.type() == ASIO_OS_DEF(SOCK_DGRAM)) + service_impl_.open(impl, protocol, ec); + else + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to a datagram socket. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + service_impl_.assign(impl, protocol, native_socket, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a datagram socket implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying socket. + native_handle_type release(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.release(impl, ec); + } + + /// Get the native socket implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.at_mark(impl, ec); + } + + /// Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.available(impl, ec); + } + + // Bind the datagram socket to the specified local endpoint. + ASIO_SYNC_OP_VOID bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + service_impl_.bind(impl, endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the datagram socket to the specified endpoint. + ASIO_SYNC_OP_VOID connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + service_impl_.connect(impl, peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + template + ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void (asio::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(ConnectHandler) handler) + { + async_completion init(handler); + + service_impl_.async_connect(impl, peer_endpoint, init.completion_handler); + + return init.result.get(); + } + + /// Set a socket option. + template + ASIO_SYNC_OP_VOID set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + service_impl_.set_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get a socket option. + template + ASIO_SYNC_OP_VOID get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + service_impl_.get_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + template + ASIO_SYNC_OP_VOID io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + service_impl_.io_control(impl, command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + bool non_blocking(const implementation_type& impl) const + { + return service_impl_.non_blocking(impl); + } + + /// Sets the non-blocking mode of the socket. + ASIO_SYNC_OP_VOID non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return service_impl_.native_non_blocking(impl); + } + + /// Sets the non-blocking mode of the native socket implementation. + ASIO_SYNC_OP_VOID native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.native_non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.remote_endpoint(impl, ec); + } + + /// Disable sends or receives on the socket. + ASIO_SYNC_OP_VOID shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + service_impl_.shutdown(impl, what, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + ASIO_SYNC_OP_VOID wait(implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + service_impl_.wait(impl, w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, socket_base::wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, w, init.completion_handler); + + return init.result.get(); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send(impl, buffers, flags, ec); + } + + /// Start an asynchronous send. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + async_completion init(handler); + + service_impl_.async_send(impl, buffers, flags, init.completion_handler); + + return init.result.get(); + } + + /// Send a datagram to the specified endpoint. + template + std::size_t send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send_to(impl, buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + async_completion init(handler); + + service_impl_.async_send_to(impl, buffers, + destination, flags, init.completion_handler); + + return init.result.get(); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive(impl, buffers, flags, ec); + } + + /// Start an asynchronous receive. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + async_completion init(handler); + + service_impl_.async_receive(impl, buffers, flags, init.completion_handler); + + return init.result.get(); + } + + /// Receive a datagram with the endpoint of the sender. + template + std::size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, + ec); + } + + /// Start an asynchronous receive that will get the endpoint of the sender. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + async_completion init(handler); + + service_impl_.async_receive_from(impl, buffers, + sender_endpoint, flags, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/deadline_timer.hpp b/subprojects/asio/include/asio/deadline_timer.hpp new file mode 100644 index 000000000..5a2155474 --- /dev/null +++ b/subprojects/asio/include/asio/deadline_timer.hpp @@ -0,0 +1,38 @@ +// +// deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DEADLINE_TIMER_HPP +#define ASIO_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. +#include "asio/basic_deadline_timer.hpp" + +#include + +namespace asio { + +/// Typedef for the typical usage of timer. Uses a UTC clock. +typedef basic_deadline_timer deadline_timer; + +} // namespace asio + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_DEADLINE_TIMER_HPP diff --git a/subprojects/asio/include/asio/deadline_timer_service.hpp b/subprojects/asio/include/asio/deadline_timer_service.hpp new file mode 100644 index 000000000..2dcc83e2f --- /dev/null +++ b/subprojects/asio/include/asio/deadline_timer_service.hpp @@ -0,0 +1,173 @@ +// +// deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/deadline_timer_service.hpp" +#include "asio/io_context.hpp" +#include "asio/time_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a timer. +template > +class deadline_timer_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base< + deadline_timer_service > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The time traits type. + typedef TimeTraits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + +private: + // The type of the platform-specific implementation. + typedef detail::deadline_timer_service service_impl_type; + +public: + /// The implementation type of the deadline timer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new timer service for the specified io_context. + explicit deadline_timer_service(asio::io_context& io_context) + : asio::detail::service_base< + deadline_timer_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new timer implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a timer implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl, asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Cancels one asynchronous wait operation associated with the timer. + std::size_t cancel_one(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel_one(impl, ec); + } + + /// Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return service_impl_.expiry(impl); + } + + /// Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time, asio::error_code& ec) + { + return service_impl_.expires_at(impl, expiry_time, ec); + } + + /// Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return TimeTraits::subtract(service_impl_.expiry(impl), TimeTraits::now()); + } + + /// Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return service_impl_.expires_after(impl, expiry_time, ec); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl, asio::error_code& ec) + { + service_impl_.wait(impl, ec); + } + + // Start an asynchronous wait on the timer. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_DEADLINE_TIMER_SERVICE_HPP diff --git a/subprojects/asio/include/asio/defer.hpp b/subprojects/asio/include/asio/defer.hpp new file mode 100644 index 000000000..a0897f2f8 --- /dev/null +++ b/subprojects/asio/include/asio/defer.hpp @@ -0,0 +1,107 @@ +// +// defer.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DEFER_HPP +#define ASIO_DEFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the object's associated + * executor. The function object is queued for execution, and is never called + * from the current thread prior to returning from defer(). + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex by performing + * get_associated_executor(handler). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Performs ex.defer(std::move(handler), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) defer( + ASIO_MOVE_ARG(CompletionToken) token); + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the specified executor. + * The function object is queued for execution, and is never called from the + * current thread prior to returning from defer(). + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex1 by performing + * get_associated_executor(handler). + * + * @li Creates a work object @c w by performing make_work(ex1). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Constructs a function object @c f with a function call operator that + * performs ex1.dispatch(std::move(handler), alloc) followed by + * w.reset(). + * + * @li Performs Executor(ex).defer(std::move(f), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) defer( + const Executor& ex, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type* = 0); + +/// Submits a completion token or function object for execution. +/** + * @returns defer(ctx.get_executor(), forward(token)). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) defer( + ExecutionContext& ctx, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type* = 0); + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/defer.hpp" + +#endif // ASIO_DEFER_HPP diff --git a/subprojects/asio/include/asio/detail/array.hpp b/subprojects/asio/include/asio/detail/array.hpp new file mode 100644 index 000000000..ba4297462 --- /dev/null +++ b/subprojects/asio/include/asio/detail/array.hpp @@ -0,0 +1,38 @@ +// +// detail/array.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ARRAY_HPP +#define ASIO_DETAIL_ARRAY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_ARRAY) +# include +#else // defined(ASIO_HAS_STD_ARRAY) +# include +#endif // defined(ASIO_HAS_STD_ARRAY) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_ARRAY) +using std::array; +#else // defined(ASIO_HAS_STD_ARRAY) +using boost::array; +#endif // defined(ASIO_HAS_STD_ARRAY) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_ARRAY_HPP diff --git a/subprojects/asio/include/asio/detail/array_fwd.hpp b/subprojects/asio/include/asio/detail/array_fwd.hpp new file mode 100644 index 000000000..4161db0db --- /dev/null +++ b/subprojects/asio/include/asio/detail/array_fwd.hpp @@ -0,0 +1,34 @@ +// +// detail/array_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ARRAY_FWD_HPP +#define ASIO_DETAIL_ARRAY_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace boost { + +template +class array; + +} // namespace boost + +// Standard library components can't be forward declared, so we'll have to +// include the array header. Fortunately, it's fairly lightweight and doesn't +// add significantly to the compile time. +#if defined(ASIO_HAS_STD_ARRAY) +# include +#endif // defined(ASIO_HAS_STD_ARRAY) + +#endif // ASIO_DETAIL_ARRAY_FWD_HPP diff --git a/subprojects/asio/include/asio/detail/assert.hpp b/subprojects/asio/include/asio/detail/assert.hpp new file mode 100644 index 000000000..a952a441f --- /dev/null +++ b/subprojects/asio/include/asio/detail/assert.hpp @@ -0,0 +1,32 @@ +// +// detail/assert.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ASSERT_HPP +#define ASIO_DETAIL_ASSERT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_ASSERT) +# include +#else // defined(ASIO_HAS_BOOST_ASSERT) +# include +#endif // defined(ASIO_HAS_BOOST_ASSERT) + +#if defined(ASIO_HAS_BOOST_ASSERT) +# define ASIO_ASSERT(expr) BOOST_ASSERT(expr) +#else // defined(ASIO_HAS_BOOST_ASSERT) +# define ASIO_ASSERT(expr) assert(expr) +#endif // defined(ASIO_HAS_BOOST_ASSERT) + +#endif // ASIO_DETAIL_ASSERT_HPP diff --git a/subprojects/asio/include/asio/detail/atomic_count.hpp b/subprojects/asio/include/asio/detail/atomic_count.hpp new file mode 100644 index 000000000..2bf75a535 --- /dev/null +++ b/subprojects/asio/include/asio/detail/atomic_count.hpp @@ -0,0 +1,45 @@ +// +// detail/atomic_count.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ATOMIC_COUNT_HPP +#define ASIO_DETAIL_ATOMIC_COUNT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +// Nothing to include. +#elif defined(ASIO_HAS_STD_ATOMIC) +# include +#else // defined(ASIO_HAS_STD_ATOMIC) +# include +#endif // defined(ASIO_HAS_STD_ATOMIC) + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef long atomic_count; +inline void increment(atomic_count& a, long b) { a += b; } +#elif defined(ASIO_HAS_STD_ATOMIC) +typedef std::atomic atomic_count; +inline void increment(atomic_count& a, long b) { a += b; } +#else // defined(ASIO_HAS_STD_ATOMIC) +typedef boost::detail::atomic_count atomic_count; +inline void increment(atomic_count& a, long b) { while (b > 0) ++a, --b; } +#endif // defined(ASIO_HAS_STD_ATOMIC) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_ATOMIC_COUNT_HPP diff --git a/subprojects/asio/include/asio/detail/base_from_completion_cond.hpp b/subprojects/asio/include/asio/detail/base_from_completion_cond.hpp new file mode 100644 index 000000000..73b4a957f --- /dev/null +++ b/subprojects/asio/include/asio/detail/base_from_completion_cond.hpp @@ -0,0 +1,68 @@ +// +// detail/base_from_completion_cond.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP +#define ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/completion_condition.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class base_from_completion_cond +{ +protected: + explicit base_from_completion_cond(CompletionCondition completion_condition) + : completion_condition_(completion_condition) + { + } + + std::size_t check_for_completion( + const asio::error_code& ec, + std::size_t total_transferred) + { + return detail::adapt_completion_condition_result( + completion_condition_(ec, total_transferred)); + } + +private: + CompletionCondition completion_condition_; +}; + +template <> +class base_from_completion_cond +{ +protected: + explicit base_from_completion_cond(transfer_all_t) + { + } + + static std::size_t check_for_completion( + const asio::error_code& ec, + std::size_t total_transferred) + { + return transfer_all_t()(ec, total_transferred); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP diff --git a/subprojects/asio/include/asio/detail/bind_handler.hpp b/subprojects/asio/include/asio/detail/bind_handler.hpp new file mode 100644 index 000000000..0f4f066d8 --- /dev/null +++ b/subprojects/asio/include/asio/detail/bind_handler.hpp @@ -0,0 +1,816 @@ +// +// detail/bind_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BIND_HANDLER_HPP +#define ASIO_DETAIL_BIND_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class binder1 +{ +public: + template + binder1(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1) + { + } + + binder1(Handler& handler, const Arg1& arg1) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1) + { + } + +#if defined(ASIO_HAS_MOVE) + binder1(const binder1& other) + : handler_(other.handler_), + arg1_(other.arg1_) + { + } + + binder1(binder1&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_)); + } + + void operator()() const + { + handler_(arg1_); + } + +//private: + Handler handler_; + Arg1 arg1_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder1* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder1* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + binder1* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline binder1::type, Arg1> bind_handler( + ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1) +{ + return binder1::type, Arg1>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1); +} + +template +class binder2 +{ +public: + template + binder2(int, ASIO_MOVE_ARG(T) handler, + const Arg1& arg1, const Arg2& arg2) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2) + { + } + + binder2(Handler& handler, const Arg1& arg1, const Arg2& arg2) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2) + { + } + +#if defined(ASIO_HAS_MOVE) + binder2(const binder2& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_) + { + } + + binder2(binder2&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_)); + } + + void operator()() const + { + handler_(arg1_, arg2_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder2* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder2* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + binder2* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline binder2::type, Arg1, Arg2> bind_handler( + ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, const Arg2& arg2) +{ + return binder2::type, Arg1, Arg2>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2); +} + +template +class binder3 +{ +public: + template + binder3(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + + binder3(Handler& handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + +#if defined(ASIO_HAS_MOVE) + binder3(const binder3& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_), + arg3_(other.arg3_) + { + } + + binder3(binder3&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), + arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_), static_cast(arg3_)); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder3* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder3* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + binder3* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + binder3* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder3* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline binder3::type, Arg1, Arg2, Arg3> bind_handler( + ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3) +{ + return binder3::type, Arg1, Arg2, Arg3>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3); +} + +template +class binder4 +{ +public: + template + binder4(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + + binder4(Handler& handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + +#if defined(ASIO_HAS_MOVE) + binder4(const binder4& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_), + arg3_(other.arg3_), + arg4_(other.arg4_) + { + } + + binder4(binder4&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), + arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)), + arg4_(ASIO_MOVE_CAST(Arg4)(other.arg4_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_), static_cast(arg3_), + static_cast(arg4_)); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder4* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder4* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + binder4* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + binder4* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder4* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline binder4::type, Arg1, Arg2, Arg3, Arg4> +bind_handler(ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) +{ + return binder4::type, Arg1, Arg2, Arg3, Arg4>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3, arg4); +} + +template +class binder5 +{ +public: + template + binder5(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + + binder5(Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + +#if defined(ASIO_HAS_MOVE) + binder5(const binder5& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_), + arg3_(other.arg3_), + arg4_(other.arg4_), + arg5_(other.arg5_) + { + } + + binder5(binder5&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), + arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)), + arg4_(ASIO_MOVE_CAST(Arg4)(other.arg4_)), + arg5_(ASIO_MOVE_CAST(Arg5)(other.arg5_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_), static_cast(arg3_), + static_cast(arg4_), static_cast(arg5_)); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; + Arg5 arg5_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder5* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder5* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + binder5* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + binder5* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder5* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline binder5::type, Arg1, Arg2, Arg3, Arg4, Arg5> +bind_handler(ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) +{ + return binder5::type, Arg1, Arg2, Arg3, Arg4, Arg5>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3, arg4, arg5); +} + +#if defined(ASIO_HAS_MOVE) + +template +class move_binder1 +{ +public: + move_binder1(int, ASIO_MOVE_ARG(Handler) handler, + ASIO_MOVE_ARG(Arg1) arg1) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(ASIO_MOVE_CAST(Arg1)(arg1)) + { + } + + move_binder1(move_binder1&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)) + { + } + + void operator()() + { + handler_(ASIO_MOVE_CAST(Arg1)(arg1_)); + } + +//private: + Handler handler_; + Arg1 arg1_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + move_binder1* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + move_binder1* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + move_binder1* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(ASIO_MOVE_ARG(Function) function, + move_binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + ASIO_MOVE_CAST(Function)(function), this_handler->handler_); +} + +template +class move_binder2 +{ +public: + move_binder2(int, ASIO_MOVE_ARG(Handler) handler, + const Arg1& arg1, ASIO_MOVE_ARG(Arg2) arg2) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(ASIO_MOVE_CAST(Arg2)(arg2)) + { + } + + move_binder2(move_binder2&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)) + { + } + + void operator()() + { + handler_(static_cast(arg1_), + ASIO_MOVE_CAST(Arg2)(arg2_)); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + move_binder2* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + move_binder2* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + move_binder2* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(ASIO_MOVE_ARG(Function) function, + move_binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + ASIO_MOVE_CAST(Function)(function), this_handler->handler_); +} + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace detail + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::binder1& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::binder2& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const detail::binder1& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +template +struct associated_executor, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const detail::binder2& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#if defined(ASIO_HAS_MOVE) + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::move_binder1& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_allocator< + detail::move_binder2, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::move_binder2& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const detail::move_binder1& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +template +struct associated_executor, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const detail::move_binder2& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BIND_HANDLER_HPP diff --git a/subprojects/asio/include/asio/detail/buffer_resize_guard.hpp b/subprojects/asio/include/asio/detail/buffer_resize_guard.hpp new file mode 100644 index 000000000..58ebc4cc7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/buffer_resize_guard.hpp @@ -0,0 +1,66 @@ +// +// detail/buffer_resize_guard.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP +#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/limits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to manage buffer resizing in an exception safe way. +template +class buffer_resize_guard +{ +public: + // Constructor. + buffer_resize_guard(Buffer& buffer) + : buffer_(buffer), + old_size_(buffer.size()) + { + } + + // Destructor rolls back the buffer resize unless commit was called. + ~buffer_resize_guard() + { + if (old_size_ != (std::numeric_limits::max)()) + { + buffer_.resize(old_size_); + } + } + + // Commit the resize transaction. + void commit() + { + old_size_ = (std::numeric_limits::max)(); + } + +private: + // The buffer being managed. + Buffer& buffer_; + + // The size of the buffer at the time the guard was constructed. + size_t old_size_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP diff --git a/subprojects/asio/include/asio/detail/buffer_sequence_adapter.hpp b/subprojects/asio/include/asio/detail/buffer_sequence_adapter.hpp new file mode 100644 index 000000000..92a8e3d2f --- /dev/null +++ b/subprojects/asio/include/asio/detail/buffer_sequence_adapter.hpp @@ -0,0 +1,544 @@ +// +// detail/buffer_sequence_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP +#define ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class buffer_sequence_adapter_base +{ +#if defined(ASIO_WINDOWS_RUNTIME) +public: + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 1 }; + +protected: + typedef Windows::Storage::Streams::IBuffer^ native_buffer_type; + + ASIO_DECL static void init_native_buffer( + native_buffer_type& buf, + const asio::mutable_buffer& buffer); + + ASIO_DECL static void init_native_buffer( + native_buffer_type& buf, + const asio::const_buffer& buffer); +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +public: + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; + +protected: + typedef WSABUF native_buffer_type; + + static void init_native_buffer(WSABUF& buf, + const asio::mutable_buffer& buffer) + { + buf.buf = static_cast(buffer.data()); + buf.len = static_cast(buffer.size()); + } + + static void init_native_buffer(WSABUF& buf, + const asio::const_buffer& buffer) + { + buf.buf = const_cast(static_cast(buffer.data())); + buf.len = static_cast(buffer.size()); + } +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +public: + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; + +protected: + typedef iovec native_buffer_type; + + static void init_iov_base(void*& base, void* addr) + { + base = addr; + } + + template + static void init_iov_base(T& base, void* addr) + { + base = static_cast(addr); + } + + static void init_native_buffer(iovec& iov, + const asio::mutable_buffer& buffer) + { + init_iov_base(iov.iov_base, buffer.data()); + iov.iov_len = buffer.size(); + } + + static void init_native_buffer(iovec& iov, + const asio::const_buffer& buffer) + { + init_iov_base(iov.iov_base, const_cast(buffer.data())); + iov.iov_len = buffer.size(); + } +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +}; + +// Helper class to translate buffers into the native buffer representation. +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter(const Buffers& buffer_sequence) + : count_(0), total_buffer_size_(0) + { + buffer_sequence_adapter::init( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + + native_buffer_type* buffers() + { + return buffers_; + } + + std::size_t count() const + { + return count_; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const Buffers& buffer_sequence) + { + return buffer_sequence_adapter::all_empty( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + + static void validate(const Buffers& buffer_sequence) + { + buffer_sequence_adapter::validate( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + + static Buffer first(const Buffers& buffer_sequence) + { + return buffer_sequence_adapter::first( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + +private: + template + void init(Iterator begin, Iterator end) + { + Iterator iter = begin; + for (; iter != end && count_ < max_buffers; ++iter, ++count_) + { + Buffer buffer(*iter); + init_native_buffer(buffers_[count_], buffer); + total_buffer_size_ += buffer.size(); + } + } + + template + static bool all_empty(Iterator begin, Iterator end) + { + Iterator iter = begin; + std::size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + if (Buffer(*iter).size() > 0) + return false; + return true; + } + + template + static void validate(Iterator begin, Iterator end) + { + Iterator iter = begin; + for (; iter != end; ++iter) + { + Buffer buffer(*iter); + buffer.data(); + } + } + + template + static Buffer first(Iterator begin, Iterator end) + { + Iterator iter = begin; + for (; iter != end; ++iter) + { + Buffer buffer(*iter); + if (buffer.size() != 0) + return buffer; + } + return Buffer(); + } + + native_buffer_type buffers_[max_buffers]; + std::size_t count_; + std::size_t total_buffer_size_; +}; + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const asio::mutable_buffer& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::mutable_buffer& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::mutable_buffer& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::mutable_buffer& buffer_sequence) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const asio::const_buffer& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::const_buffer& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::const_buffer& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::const_buffer& buffer_sequence) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const asio::mutable_buffers_1& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::mutable_buffers_1& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::mutable_buffers_1& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::mutable_buffers_1& buffer_sequence) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const asio::const_buffers_1& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::const_buffers_1& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::const_buffers_1& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::const_buffers_1& buffer_sequence) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +class buffer_sequence_adapter > + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const boost::array& buffer_sequence) + { + init_native_buffer(buffers_[0], Buffer(buffer_sequence[0])); + init_native_buffer(buffers_[1], Buffer(buffer_sequence[1])); + total_buffer_size_ = buffer_sequence[0].size() + buffer_sequence[1].size(); + } + + native_buffer_type* buffers() + { + return buffers_; + } + + std::size_t count() const + { + return 2; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const boost::array& buffer_sequence) + { + return buffer_sequence[0].size() == 0 && buffer_sequence[1].size() == 0; + } + + static void validate(const boost::array& buffer_sequence) + { + buffer_sequence[0].data(); + buffer_sequence[1].data(); + } + + static Buffer first(const boost::array& buffer_sequence) + { + return Buffer(buffer_sequence[0].size() != 0 + ? buffer_sequence[0] : buffer_sequence[1]); + } + +private: + native_buffer_type buffers_[2]; + std::size_t total_buffer_size_; +}; + +#if defined(ASIO_HAS_STD_ARRAY) + +template +class buffer_sequence_adapter > + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const std::array& buffer_sequence) + { + init_native_buffer(buffers_[0], Buffer(buffer_sequence[0])); + init_native_buffer(buffers_[1], Buffer(buffer_sequence[1])); + total_buffer_size_ = buffer_sequence[0].size() + buffer_sequence[1].size(); + } + + native_buffer_type* buffers() + { + return buffers_; + } + + std::size_t count() const + { + return 2; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const std::array& buffer_sequence) + { + return buffer_sequence[0].size() == 0 && buffer_sequence[1].size() == 0; + } + + static void validate(const std::array& buffer_sequence) + { + buffer_sequence[0].data(); + buffer_sequence[1].data(); + } + + static Buffer first(const std::array& buffer_sequence) + { + return Buffer(buffer_sequence[0].size() != 0 + ? buffer_sequence[0] : buffer_sequence[1]); + } + +private: + native_buffer_type buffers_[2]; + std::size_t total_buffer_size_; +}; + +#endif // defined(ASIO_HAS_STD_ARRAY) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/buffer_sequence_adapter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP diff --git a/subprojects/asio/include/asio/detail/buffered_stream_storage.hpp b/subprojects/asio/include/asio/detail/buffered_stream_storage.hpp new file mode 100644 index 000000000..c5eb081d9 --- /dev/null +++ b/subprojects/asio/include/asio/detail/buffered_stream_storage.hpp @@ -0,0 +1,126 @@ +// +// detail/buffered_stream_storage.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP +#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/assert.hpp" +#include +#include +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class buffered_stream_storage +{ +public: + // The type of the bytes stored in the buffer. + typedef unsigned char byte_type; + + // The type used for offsets into the buffer. + typedef std::size_t size_type; + + // Constructor. + explicit buffered_stream_storage(std::size_t buffer_capacity) + : begin_offset_(0), + end_offset_(0), + buffer_(buffer_capacity) + { + } + + /// Clear the buffer. + void clear() + { + begin_offset_ = 0; + end_offset_ = 0; + } + + // Return a pointer to the beginning of the unread data. + mutable_buffer data() + { + return asio::buffer(buffer_) + begin_offset_; + } + + // Return a pointer to the beginning of the unread data. + const_buffer data() const + { + return asio::buffer(buffer_) + begin_offset_; + } + + // Is there no unread data in the buffer. + bool empty() const + { + return begin_offset_ == end_offset_; + } + + // Return the amount of unread data the is in the buffer. + size_type size() const + { + return end_offset_ - begin_offset_; + } + + // Resize the buffer to the specified length. + void resize(size_type length) + { + ASIO_ASSERT(length <= capacity()); + if (begin_offset_ + length <= capacity()) + { + end_offset_ = begin_offset_ + length; + } + else + { + using namespace std; // For memmove. + memmove(&buffer_[0], &buffer_[0] + begin_offset_, size()); + end_offset_ = length; + begin_offset_ = 0; + } + } + + // Return the maximum size for data in the buffer. + size_type capacity() const + { + return buffer_.size(); + } + + // Consume multiple bytes from the beginning of the buffer. + void consume(size_type count) + { + ASIO_ASSERT(begin_offset_ + count <= end_offset_); + begin_offset_ += count; + if (empty()) + clear(); + } + +private: + // The offset to the beginning of the unread data. + size_type begin_offset_; + + // The offset to the end of the unread data. + size_type end_offset_; + + // The data in the buffer. + std::vector buffer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP diff --git a/subprojects/asio/include/asio/detail/call_stack.hpp b/subprojects/asio/include/asio/detail/call_stack.hpp new file mode 100644 index 000000000..5725a10a2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/call_stack.hpp @@ -0,0 +1,125 @@ +// +// detail/call_stack.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CALL_STACK_HPP +#define ASIO_DETAIL_CALL_STACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/tss_ptr.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to determine whether or not the current thread is inside an +// invocation of io_context::run() for a specified io_context object. +template +class call_stack +{ +public: + // Context class automatically pushes the key/value pair on to the stack. + class context + : private noncopyable + { + public: + // Push the key on to the stack. + explicit context(Key* k) + : key_(k), + next_(call_stack::top_) + { + value_ = reinterpret_cast(this); + call_stack::top_ = this; + } + + // Push the key/value pair on to the stack. + context(Key* k, Value& v) + : key_(k), + value_(&v), + next_(call_stack::top_) + { + call_stack::top_ = this; + } + + // Pop the key/value pair from the stack. + ~context() + { + call_stack::top_ = next_; + } + + // Find the next context with the same key. + Value* next_by_key() const + { + context* elem = next_; + while (elem) + { + if (elem->key_ == key_) + return elem->value_; + elem = elem->next_; + } + return 0; + } + + private: + friend class call_stack; + + // The key associated with the context. + Key* key_; + + // The value associated with the context. + Value* value_; + + // The next element in the stack. + context* next_; + }; + + friend class context; + + // Determine whether the specified owner is on the stack. Returns address of + // key if present, 0 otherwise. + static Value* contains(Key* k) + { + context* elem = top_; + while (elem) + { + if (elem->key_ == k) + return elem->value_; + elem = elem->next_; + } + return 0; + } + + // Obtain the value at the top of the stack. + static Value* top() + { + context* elem = top_; + return elem ? elem->value_ : 0; + } + +private: + // The top of the stack of calls for the current thread. + static tss_ptr top_; +}; + +template +tss_ptr::context> +call_stack::top_; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CALL_STACK_HPP diff --git a/subprojects/asio/include/asio/detail/chrono.hpp b/subprojects/asio/include/asio/detail/chrono.hpp new file mode 100644 index 000000000..8f56beed5 --- /dev/null +++ b/subprojects/asio/include/asio/detail/chrono.hpp @@ -0,0 +1,66 @@ +// +// detail/chrono.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CHRONO_HPP +#define ASIO_DETAIL_CHRONO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_CHRONO) +# include +#elif defined(ASIO_HAS_BOOST_CHRONO) +# include +#endif // defined(ASIO_HAS_BOOST_CHRONO) + +namespace asio { +namespace chrono { + +#if defined(ASIO_HAS_STD_CHRONO) +using std::chrono::duration; +using std::chrono::time_point; +using std::chrono::duration_cast; +using std::chrono::nanoseconds; +using std::chrono::microseconds; +using std::chrono::milliseconds; +using std::chrono::seconds; +using std::chrono::minutes; +using std::chrono::hours; +using std::chrono::time_point_cast; +#if defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) +typedef std::chrono::monotonic_clock steady_clock; +#else // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) +using std::chrono::steady_clock; +#endif // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) +using std::chrono::system_clock; +using std::chrono::high_resolution_clock; +#elif defined(ASIO_HAS_BOOST_CHRONO) +using boost::chrono::duration; +using boost::chrono::time_point; +using boost::chrono::duration_cast; +using boost::chrono::nanoseconds; +using boost::chrono::microseconds; +using boost::chrono::milliseconds; +using boost::chrono::seconds; +using boost::chrono::minutes; +using boost::chrono::hours; +using boost::chrono::time_point_cast; +using boost::chrono::system_clock; +using boost::chrono::steady_clock; +using boost::chrono::high_resolution_clock; +#endif // defined(ASIO_HAS_BOOST_CHRONO) + +} // namespace chrono +} // namespace asio + +#endif // ASIO_DETAIL_CHRONO_HPP diff --git a/subprojects/asio/include/asio/detail/chrono_time_traits.hpp b/subprojects/asio/include/asio/detail/chrono_time_traits.hpp new file mode 100644 index 000000000..d1528f70d --- /dev/null +++ b/subprojects/asio/include/asio/detail/chrono_time_traits.hpp @@ -0,0 +1,190 @@ +// +// detail/chrono_time_traits.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CHRONO_TIME_TRAITS_HPP +#define ASIO_DETAIL_CHRONO_TIME_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/cstdint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper template to compute the greatest common divisor. +template +struct gcd { enum { value = gcd::value }; }; + +template +struct gcd { enum { value = v1 }; }; + +// Adapts std::chrono clocks for use with a deadline timer. +template +struct chrono_time_traits +{ + // The clock type. + typedef Clock clock_type; + + // The duration type of the clock. + typedef typename clock_type::duration duration_type; + + // The time point type of the clock. + typedef typename clock_type::time_point time_type; + + // The period of the clock. + typedef typename duration_type::period period_type; + + // Get the current time. + static time_type now() + { + return clock_type::now(); + } + + // Add a duration to a time. + static time_type add(const time_type& t, const duration_type& d) + { + const time_type epoch; + if (t >= epoch) + { + if ((time_type::max)() - t < d) + return (time_type::max)(); + } + else // t < epoch + { + if (-(t - (time_type::min)()) > d) + return (time_type::min)(); + } + + return t + d; + } + + // Subtract one time from another. + static duration_type subtract(const time_type& t1, const time_type& t2) + { + const time_type epoch; + if (t1 >= epoch) + { + if (t2 >= epoch) + { + return t1 - t2; + } + else if (t2 == (time_type::min)()) + { + return (duration_type::max)(); + } + else if ((time_type::max)() - t1 < epoch - t2) + { + return (duration_type::max)(); + } + else + { + return t1 - t2; + } + } + else // t1 < epoch + { + if (t2 < epoch) + { + return t1 - t2; + } + else if (t1 == (time_type::min)()) + { + return (duration_type::min)(); + } + else if ((time_type::max)() - t2 < epoch - t1) + { + return (duration_type::min)(); + } + else + { + return -(t2 - t1); + } + } + } + + // Test whether one time is less than another. + static bool less_than(const time_type& t1, const time_type& t2) + { + return t1 < t2; + } + + // Implement just enough of the posix_time::time_duration interface to supply + // what the timer_queue requires. + class posix_time_duration + { + public: + explicit posix_time_duration(const duration_type& d) + : d_(d) + { + } + + int64_t ticks() const + { + return d_.count(); + } + + int64_t total_seconds() const + { + return duration_cast<1, 1>(); + } + + int64_t total_milliseconds() const + { + return duration_cast<1, 1000>(); + } + + int64_t total_microseconds() const + { + return duration_cast<1, 1000000>(); + } + + private: + template + int64_t duration_cast() const + { + const int64_t num1 = period_type::num / gcd::value; + const int64_t num2 = Num / gcd::value; + + const int64_t den1 = period_type::den / gcd::value; + const int64_t den2 = Den / gcd::value; + + const int64_t num = num1 * den2; + const int64_t den = num2 * den1; + + if (num == 1 && den == 1) + return ticks(); + else if (num != 1 && den == 1) + return ticks() * num; + else if (num == 1 && period_type::den != 1) + return ticks() / den; + else + return ticks() * num / den; + } + + duration_type d_; + }; + + // Convert to POSIX duration type. + static posix_time_duration to_posix_duration(const duration_type& d) + { + return posix_time_duration(WaitTraits::to_wait_duration(d)); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CHRONO_TIME_TRAITS_HPP diff --git a/subprojects/asio/include/asio/detail/completion_handler.hpp b/subprojects/asio/include/asio/detail/completion_handler.hpp new file mode 100644 index 000000000..58a2e6db9 --- /dev/null +++ b/subprojects/asio/include/asio/detail/completion_handler.hpp @@ -0,0 +1,83 @@ +// +// detail/completion_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_COMPLETION_HANDLER_HPP +#define ASIO_DETAIL_COMPLETION_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class completion_handler : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(completion_handler); + + completion_handler(Handler& h) + : operation(&completion_handler::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(h)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + completion_handler* h(static_cast(base)); + ptr p = { asio::detail::addressof(h->handler_), h, h }; + handler_work w(h->handler_); + + ASIO_HANDLER_COMPLETION((*h)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + Handler handler(ASIO_MOVE_CAST(Handler)(h->handler_)); + p.h = asio::detail::addressof(handler); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN(()); + w.complete(handler, handler); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_COMPLETION_HANDLER_HPP diff --git a/subprojects/asio/include/asio/detail/concurrency_hint.hpp b/subprojects/asio/include/asio/detail/concurrency_hint.hpp new file mode 100644 index 000000000..229124d37 --- /dev/null +++ b/subprojects/asio/include/asio/detail/concurrency_hint.hpp @@ -0,0 +1,94 @@ +// +// detail/concurrency_hint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONCURRENCY_HINT_HPP +#define ASIO_DETAIL_CONCURRENCY_HINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" + +// The concurrency hint ID and mask are used to identify when a "well-known" +// concurrency hint value has been passed to the io_context. +#define ASIO_CONCURRENCY_HINT_ID 0xA5100000u +#define ASIO_CONCURRENCY_HINT_ID_MASK 0xFFFF0000u + +// If set, this bit indicates that the scheduler should perform locking. +#define ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER 0x1u + +// If set, this bit indicates that the reactor should perform locking when +// managing descriptor registrations. +#define ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION 0x2u + +// If set, this bit indicates that the reactor should perform locking for I/O. +#define ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO 0x4u + +// Helper macro to determine if we have a special concurrency hint. +#define ASIO_CONCURRENCY_HINT_IS_SPECIAL(hint) \ + ((static_cast(hint) \ + & ASIO_CONCURRENCY_HINT_ID_MASK) \ + == ASIO_CONCURRENCY_HINT_ID) + +// Helper macro to determine if locking is enabled for a given facility. +#define ASIO_CONCURRENCY_HINT_IS_LOCKING(facility, hint) \ + (((static_cast(hint) \ + & (ASIO_CONCURRENCY_HINT_ID_MASK \ + | ASIO_CONCURRENCY_HINT_LOCKING_ ## facility)) \ + ^ ASIO_CONCURRENCY_HINT_ID) != 0) + +// This special concurrency hint disables locking in both the scheduler and +// reactor I/O. This hint has the following restrictions: +// +// - Care must be taken to ensure that all operations on the io_context and any +// of its associated I/O objects (such as sockets and timers) occur in only +// one thread at a time. +// +// - Asynchronous resolve operations fail with operation_not_supported. +// +// - If a signal_set is used with the io_context, signal_set objects cannot be +// used with any other io_context in the program. +#define ASIO_CONCURRENCY_HINT_UNSAFE \ + static_cast(ASIO_CONCURRENCY_HINT_ID) + +// This special concurrency hint disables locking in the reactor I/O. This hint +// has the following restrictions: +// +// - Care must be taken to ensure that run functions on the io_context, and all +// operations on the io_context's associated I/O objects (such as sockets and +// timers), occur in only one thread at a time. +#define ASIO_CONCURRENCY_HINT_UNSAFE_IO \ + static_cast(ASIO_CONCURRENCY_HINT_ID \ + | ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER \ + | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION) + +// The special concurrency hint provides full thread safety. +#define ASIO_CONCURRENCY_HINT_SAFE \ + static_cast(ASIO_CONCURRENCY_HINT_ID \ + | ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER \ + | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION \ + | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO) + +// This #define may be overridden at compile time to specify a program-wide +// default concurrency hint, used by the zero-argument io_context constructor. +#if !defined(ASIO_CONCURRENCY_HINT_DEFAULT) +# define ASIO_CONCURRENCY_HINT_DEFAULT -1 +#endif // !defined(ASIO_CONCURRENCY_HINT_DEFAULT) + +// This #define may be overridden at compile time to specify a program-wide +// concurrency hint, used by the one-argument io_context constructor when +// passed a value of 1. +#if !defined(ASIO_CONCURRENCY_HINT_1) +# define ASIO_CONCURRENCY_HINT_1 1 +#endif // !defined(ASIO_CONCURRENCY_HINT_DEFAULT) + +#endif // ASIO_DETAIL_CONCURRENCY_HINT_HPP diff --git a/subprojects/asio/include/asio/detail/conditionally_enabled_event.hpp b/subprojects/asio/include/asio/detail/conditionally_enabled_event.hpp new file mode 100644 index 000000000..0fda401e3 --- /dev/null +++ b/subprojects/asio/include/asio/detail/conditionally_enabled_event.hpp @@ -0,0 +1,112 @@ +// +// detail/conditionally_enabled_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP +#define ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/conditionally_enabled_mutex.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/null_event.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Mutex adapter used to conditionally enable or disable locking. +class conditionally_enabled_event + : private noncopyable +{ +public: + // Constructor. + conditionally_enabled_event() + { + } + + // Destructor. + ~conditionally_enabled_event() + { + } + + // Signal the event. (Retained for backward compatibility.) + void signal(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.signal(lock); + } + + // Signal all waiters. + void signal_all(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.signal_all(lock); + } + + // Unlock the mutex and signal one waiter. + void unlock_and_signal_one( + conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.unlock_and_signal_one(lock); + } + + // If there's a waiter, unlock the mutex and signal it. + bool maybe_unlock_and_signal_one( + conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + return event_.maybe_unlock_and_signal_one(lock); + else + return false; + } + + // Reset the event. + void clear(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.clear(lock); + } + + // Wait for the event to become signalled. + void wait(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.wait(lock); + else + null_event().wait(lock); + } + + // Timed wait for the event to become signalled. + bool wait_for_usec( + conditionally_enabled_mutex::scoped_lock& lock, long usec) + { + if (lock.mutex_.enabled_) + return event_.wait_for_usec(lock, usec); + else + return null_event().wait_for_usec(lock, usec); + } + +private: + asio::detail::event event_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP diff --git a/subprojects/asio/include/asio/detail/conditionally_enabled_mutex.hpp b/subprojects/asio/include/asio/detail/conditionally_enabled_mutex.hpp new file mode 100644 index 000000000..2872db98b --- /dev/null +++ b/subprojects/asio/include/asio/detail/conditionally_enabled_mutex.hpp @@ -0,0 +1,149 @@ +// +// detail/conditionally_enabled_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONDITIONALLY_ENABLED_MUTEX_HPP +#define ASIO_DETAIL_CONDITIONALLY_ENABLED_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Mutex adapter used to conditionally enable or disable locking. +class conditionally_enabled_mutex + : private noncopyable +{ +public: + // Helper class to lock and unlock a mutex automatically. + class scoped_lock + : private noncopyable + { + public: + // Tag type used to distinguish constructors. + enum adopt_lock_t { adopt_lock }; + + // Constructor adopts a lock that is already held. + scoped_lock(conditionally_enabled_mutex& m, adopt_lock_t) + : mutex_(m), + locked_(m.enabled_) + { + } + + // Constructor acquires the lock. + explicit scoped_lock(conditionally_enabled_mutex& m) + : mutex_(m) + { + if (m.enabled_) + { + mutex_.mutex_.lock(); + locked_ = true; + } + else + locked_ = false; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (mutex_.enabled_ && !locked_) + { + mutex_.mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + + // Test whether the lock is held. + bool locked() const + { + return locked_; + } + + // Get the underlying mutex. + asio::detail::mutex& mutex() + { + return mutex_.mutex_; + } + + private: + friend class conditionally_enabled_event; + conditionally_enabled_mutex& mutex_; + bool locked_; + }; + + // Constructor. + explicit conditionally_enabled_mutex(bool enabled) + : enabled_(enabled) + { + } + + // Destructor. + ~conditionally_enabled_mutex() + { + } + + // Determine whether locking is enabled. + bool enabled() const + { + return enabled_; + } + + // Lock the mutex. + void lock() + { + if (enabled_) + mutex_.lock(); + } + + // Unlock the mutex. + void unlock() + { + if (enabled_) + mutex_.unlock(); + } + +private: + friend class scoped_lock; + friend class conditionally_enabled_event; + asio::detail::mutex mutex_; + const bool enabled_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONDITIONALLY_ENABLED_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/config.hpp b/subprojects/asio/include/asio/detail/config.hpp new file mode 100644 index 000000000..e54eea744 --- /dev/null +++ b/subprojects/asio/include/asio/detail/config.hpp @@ -0,0 +1,1456 @@ +// +// detail/config.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONFIG_HPP +#define ASIO_DETAIL_CONFIG_HPP + +#if defined(ASIO_STANDALONE) +# define ASIO_DISABLE_BOOST_ARRAY 1 +# define ASIO_DISABLE_BOOST_ASSERT 1 +# define ASIO_DISABLE_BOOST_BIND 1 +# define ASIO_DISABLE_BOOST_CHRONO 1 +# define ASIO_DISABLE_BOOST_DATE_TIME 1 +# define ASIO_DISABLE_BOOST_LIMITS 1 +# define ASIO_DISABLE_BOOST_REGEX 1 +# define ASIO_DISABLE_BOOST_STATIC_CONSTANT 1 +# define ASIO_DISABLE_BOOST_THROW_EXCEPTION 1 +# define ASIO_DISABLE_BOOST_WORKAROUND 1 +#else // defined(ASIO_STANDALONE) +# include +# include +# define ASIO_HAS_BOOST_CONFIG 1 +#endif // defined(ASIO_STANDALONE) + +// Default to a header-only implementation. The user must specifically request +// separate compilation by defining either ASIO_SEPARATE_COMPILATION or +// ASIO_DYN_LINK (as a DLL/shared library implies separate compilation). +#if !defined(ASIO_HEADER_ONLY) +# if !defined(ASIO_SEPARATE_COMPILATION) +# if !defined(ASIO_DYN_LINK) +# define ASIO_HEADER_ONLY 1 +# endif // !defined(ASIO_DYN_LINK) +# endif // !defined(ASIO_SEPARATE_COMPILATION) +#endif // !defined(ASIO_HEADER_ONLY) + +#if defined(ASIO_HEADER_ONLY) +# define ASIO_DECL inline +#else // defined(ASIO_HEADER_ONLY) +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__) +// We need to import/export our code only if the user has specifically asked +// for it by defining ASIO_DYN_LINK. +# if defined(ASIO_DYN_LINK) +// Export if this is our own source, otherwise import. +# if defined(ASIO_SOURCE) +# define ASIO_DECL __declspec(dllexport) +# else // defined(ASIO_SOURCE) +# define ASIO_DECL __declspec(dllimport) +# endif // defined(ASIO_SOURCE) +# endif // defined(ASIO_DYN_LINK) +# endif // defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__) +#endif // defined(ASIO_HEADER_ONLY) + +// If ASIO_DECL isn't defined yet define it now. +#if !defined(ASIO_DECL) +# define ASIO_DECL +#endif // !defined(ASIO_DECL) + +// Microsoft Visual C++ detection. +#if !defined(ASIO_MSVC) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_MSVC) +# define ASIO_MSVC BOOST_MSVC +# elif defined(_MSC_VER) && (defined(__INTELLISENSE__) \ + || (!defined(__MWERKS__) && !defined(__EDG_VERSION__))) +# define ASIO_MSVC _MSC_VER +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_MSVC) +#endif // !defined(ASIO_MSVC) + +// Clang / libc++ detection. +#if defined(__clang__) +# if (__cplusplus >= 201103) +# if __has_include(<__config>) +# include <__config> +# if defined(_LIBCPP_VERSION) +# define ASIO_HAS_CLANG_LIBCXX 1 +# endif // defined(_LIBCPP_VERSION) +# endif // __has_include(<__config>) +# endif // (__cplusplus >= 201103) +#endif // defined(__clang__) + +// Android platform detection. +#if defined(__ANDROID__) +# include +#endif // defined(__ANDROID__) + +// Support move construction and assignment on compilers known to allow it. +#if !defined(ASIO_HAS_MOVE) +# if !defined(ASIO_DISABLE_MOVE) +# if defined(__clang__) +# if __has_feature(__cxx_rvalue_references__) +# define ASIO_HAS_MOVE 1 +# endif // __has_feature(__cxx_rvalue_references__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_MOVE 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_MOVE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# if defined(__INTEL_CXX11_MODE__) +# if defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500) +# define BOOST_ASIO_HAS_MOVE 1 +# endif // defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500) +# if defined(__ICL) && (__ICL >= 1500) +# define BOOST_ASIO_HAS_MOVE 1 +# endif // defined(__ICL) && (__ICL >= 1500) +# endif // defined(__INTEL_CXX11_MODE__) +# endif // !defined(ASIO_DISABLE_MOVE) +#endif // !defined(ASIO_HAS_MOVE) + +// If ASIO_MOVE_CAST isn't defined, and move support is available, define +// ASIO_MOVE_ARG and ASIO_MOVE_CAST to take advantage of rvalue +// references and perfect forwarding. +#if defined(ASIO_HAS_MOVE) && !defined(ASIO_MOVE_CAST) +# define ASIO_MOVE_ARG(type) type&& +# define ASIO_MOVE_ARG2(type1, type2) type1, type2&& +# define ASIO_MOVE_CAST(type) static_cast +# define ASIO_MOVE_CAST2(type1, type2) static_cast +#endif // defined(ASIO_HAS_MOVE) && !defined(ASIO_MOVE_CAST) + +// If ASIO_MOVE_CAST still isn't defined, default to a C++03-compatible +// implementation. Note that older g++ and MSVC versions don't like it when you +// pass a non-member function through a const reference, so for most compilers +// we'll play it safe and stick with the old approach of passing the handler by +// value. +#if !defined(ASIO_MOVE_CAST) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4) +# define ASIO_MOVE_ARG(type) const type& +# else // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4) +# define ASIO_MOVE_ARG(type) type +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1400) +# define ASIO_MOVE_ARG(type) const type& +# else // (_MSC_VER >= 1400) +# define ASIO_MOVE_ARG(type) type +# endif // (_MSC_VER >= 1400) +# else +# define ASIO_MOVE_ARG(type) type +# endif +# define ASIO_MOVE_CAST(type) static_cast +# define ASIO_MOVE_CAST2(type1, type2) static_cast +#endif // !defined(ASIO_MOVE_CAST) + +// Support variadic templates on compilers known to allow it. +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) +# if !defined(ASIO_DISABLE_VARIADIC_TEMPLATES) +# if defined(__clang__) +# if __has_feature(__cxx_variadic_templates__) +# define ASIO_HAS_VARIADIC_TEMPLATES 1 +# endif // __has_feature(__cxx_variadic_templates__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_VARIADIC_TEMPLATES 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_VARIADIC_TEMPLATES 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +// Support deleted functions on compilers known to allow it. +#if !defined(ASIO_DELETED) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_DELETED = delete +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(__clang__) +# if __has_feature(__cxx_deleted_functions__) +# define ASIO_DELETED = delete +# endif // __has_feature(__cxx_deleted_functions__) +# endif // defined(__clang__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_DELETED = delete +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# if !defined(ASIO_DELETED) +# define ASIO_DELETED +# endif // !defined(ASIO_DELETED) +#endif // !defined(ASIO_DELETED) + +// Support constexpr on compilers known to allow it. +#if !defined(ASIO_HAS_CONSTEXPR) +# if !defined(ASIO_DISABLE_CONSTEXPR) +# if defined(__clang__) +# if __has_feature(__cxx_constexpr__) +# define ASIO_HAS_CONSTEXPR 1 +# endif // __has_feature(__cxx_constexr__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_CONSTEXPR 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_CONSTEXPR 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CONSTEXPR) +#endif // !defined(ASIO_HAS_CONSTEXPR) +#if !defined(ASIO_CONSTEXPR) +# if defined(ASIO_HAS_CONSTEXPR) +# define ASIO_CONSTEXPR constexpr +# else // defined(ASIO_HAS_CONSTEXPR) +# define ASIO_CONSTEXPR +# endif // defined(ASIO_HAS_CONSTEXPR) +#endif // !defined(ASIO_CONSTEXPR) + +// Support noexcept on compilers known to allow it. +#if !defined(ASIO_NOEXCEPT) +# if !defined(ASIO_DISABLE_NOEXCEPT) +# if defined(ASIO_HAS_BOOST_CONFIG) && (BOOST_VERSION >= 105300) +# define ASIO_NOEXCEPT BOOST_NOEXCEPT +# define ASIO_NOEXCEPT_OR_NOTHROW BOOST_NOEXCEPT_OR_NOTHROW +# elif defined(__clang__) +# if __has_feature(__cxx_noexcept__) +# define ASIO_NOEXCEPT noexcept(true) +# define ASIO_NOEXCEPT_OR_NOTHROW noexcept(true) +# endif // __has_feature(__cxx_noexcept__) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_NOEXCEPT noexcept(true) +# define ASIO_NOEXCEPT_OR_NOTHROW noexcept(true) +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_NOEXCEPT noexcept(true) +# define ASIO_NOEXCEPT_OR_NOTHROW noexcept(true) +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_NOEXCEPT) +# if !defined(ASIO_NOEXCEPT) +# define ASIO_NOEXCEPT +# endif // !defined(ASIO_NOEXCEPT) +# if !defined(ASIO_NOEXCEPT_OR_NOTHROW) +# define ASIO_NOEXCEPT_OR_NOTHROW throw() +# endif // !defined(ASIO_NOEXCEPT_OR_NOTHROW) +#endif // !defined(ASIO_NOEXCEPT) + +// Support automatic type deduction on compilers known to support it. +#if !defined(ASIO_HAS_DECLTYPE) +# if !defined(ASIO_DISABLE_DECLTYPE) +# if defined(__clang__) +# if __has_feature(__cxx_decltype__) +# define ASIO_HAS_DECLTYPE 1 +# endif // __has_feature(__cxx_decltype__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_DECLTYPE 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_DECLTYPE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_DECLTYPE) +#endif // !defined(ASIO_HAS_DECLTYPE) + +// Support alias templates on compilers known to allow it. +#if !defined(ASIO_HAS_ALIAS_TEMPLATES) +# if !defined(ASIO_DISABLE_ALIAS_TEMPLATES) +# if defined(__clang__) +# if __has_feature(__cxx_alias_templates__) +# define ASIO_HAS_ALIAS_TEMPLATES 1 +# endif // __has_feature(__cxx_alias_templates__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_ALIAS_TEMPLATES 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_ALIAS_TEMPLATES 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_ALIAS_TEMPLATES) +#endif // !defined(ASIO_HAS_ALIAS_TEMPLATES) + +// Standard library support for system errors. +#if !defined(ASIO_HAS_STD_SYSTEM_ERROR) +# if !defined(ASIO_DISABLE_STD_SYSTEM_ERROR) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_SYSTEM_ERROR) +#endif // !defined(ASIO_HAS_STD_SYSTEM_ERROR) + +// Compliant C++11 compilers put noexcept specifiers on error_category members. +#if !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) +# if (BOOST_VERSION >= 105300) +# define ASIO_ERROR_CATEGORY_NOEXCEPT BOOST_NOEXCEPT +# elif defined(__clang__) +# if __has_feature(__cxx_noexcept__) +# define ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true) +# endif // __has_feature(__cxx_noexcept__) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true) +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true) +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# if !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) +# define ASIO_ERROR_CATEGORY_NOEXCEPT +# endif // !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) +#endif // !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) + +// Standard library support for arrays. +#if !defined(ASIO_HAS_STD_ARRAY) +# if !defined(ASIO_DISABLE_STD_ARRAY) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ARRAY 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_ARRAY 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ARRAY 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_HAS_STD_ARRAY 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ARRAY) +#endif // !defined(ASIO_HAS_STD_ARRAY) + +// Standard library support for shared_ptr and weak_ptr. +#if !defined(ASIO_HAS_STD_SHARED_PTR) +# if !defined(ASIO_DISABLE_STD_SHARED_PTR) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_SHARED_PTR 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_SHARED_PTR 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_SHARED_PTR 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_HAS_STD_SHARED_PTR 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_SHARED_PTR) +#endif // !defined(ASIO_HAS_STD_SHARED_PTR) + +// Standard library support for allocator_arg_t. +#if !defined(ASIO_HAS_STD_ALLOCATOR_ARG) +# if !defined(ASIO_DISABLE_STD_ALLOCATOR_ARG) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ALLOCATOR_ARG) +#endif // !defined(ASIO_HAS_STD_ALLOCATOR_ARG) + +// Standard library support for atomic operations. +#if !defined(ASIO_HAS_STD_ATOMIC) +# if !defined(ASIO_DISABLE_STD_ATOMIC) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ATOMIC 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_ATOMIC 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ATOMIC 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_ATOMIC 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ATOMIC) +#endif // !defined(ASIO_HAS_STD_ATOMIC) + +// Standard library support for chrono. Some standard libraries (such as the +// libstdc++ shipped with gcc 4.6) provide monotonic_clock as per early C++0x +// drafts, rather than the eventually standardised name of steady_clock. +#if !defined(ASIO_HAS_STD_CHRONO) +# if !defined(ASIO_DISABLE_STD_CHRONO) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_CHRONO 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_CHRONO 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_CHRONO 1 +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 6)) +# define ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK 1 +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 6)) +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_CHRONO 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_CHRONO) +#endif // !defined(ASIO_HAS_STD_CHRONO) + +// Boost support for chrono. +#if !defined(ASIO_HAS_BOOST_CHRONO) +# if !defined(ASIO_DISABLE_BOOST_CHRONO) +# if (BOOST_VERSION >= 104700) +# define ASIO_HAS_BOOST_CHRONO 1 +# endif // (BOOST_VERSION >= 104700) +# endif // !defined(ASIO_DISABLE_BOOST_CHRONO) +#endif // !defined(ASIO_HAS_BOOST_CHRONO) + +// Some form of chrono library is available. +#if !defined(ASIO_HAS_CHRONO) +# if defined(ASIO_HAS_STD_CHRONO) \ + || defined(ASIO_HAS_BOOST_CHRONO) +# define ASIO_HAS_CHRONO 1 +# endif // defined(ASIO_HAS_STD_CHRONO) + // || defined(ASIO_HAS_BOOST_CHRONO) +#endif // !defined(ASIO_HAS_CHRONO) + +// Boost support for the DateTime library. +#if !defined(ASIO_HAS_BOOST_DATE_TIME) +# if !defined(ASIO_DISABLE_BOOST_DATE_TIME) +# define ASIO_HAS_BOOST_DATE_TIME 1 +# endif // !defined(ASIO_DISABLE_BOOST_DATE_TIME) +#endif // !defined(ASIO_HAS_BOOST_DATE_TIME) + +// Standard library support for addressof. +#if !defined(ASIO_HAS_STD_ADDRESSOF) +# if !defined(ASIO_DISABLE_STD_ADDRESSOF) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ADDRESSOF 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_ADDRESSOF 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ADDRESSOF 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_ADDRESSOF 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ADDRESSOF) +#endif // !defined(ASIO_HAS_STD_ADDRESSOF) + +// Standard library support for the function class. +#if !defined(ASIO_HAS_STD_FUNCTION) +# if !defined(ASIO_DISABLE_STD_FUNCTION) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_FUNCTION 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_FUNCTION 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_FUNCTION 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_FUNCTION 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_FUNCTION) +#endif // !defined(ASIO_HAS_STD_FUNCTION) + +// Standard library support for type traits. +#if !defined(ASIO_HAS_STD_TYPE_TRAITS) +# if !defined(ASIO_DISABLE_STD_TYPE_TRAITS) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_TYPE_TRAITS) +#endif // !defined(ASIO_HAS_STD_TYPE_TRAITS) + +// Standard library support for the nullptr_t type. +#if !defined(ASIO_HAS_NULLPTR) +# if !defined(ASIO_DISABLE_NULLPTR) +# if defined(__clang__) +# if __has_feature(__cxx_nullptr__) +# define ASIO_HAS_NULLPTR 1 +# endif // __has_feature(__cxx_rvalue_references__) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_NULLPTR 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_NULLPTR 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_NULLPTR) +#endif // !defined(ASIO_HAS_NULLPTR) + +// Standard library support for the C++11 allocator additions. +#if !defined(ASIO_HAS_CXX11_ALLOCATORS) +# if !defined(ASIO_DISABLE_CXX11_ALLOCATORS) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# endif // (__cplusplus >= 201103) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1800) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# endif // (_MSC_VER >= 1800) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CXX11_ALLOCATORS) +#endif // !defined(ASIO_HAS_CXX11_ALLOCATORS) + +// Standard library support for the cstdint header. +#if !defined(ASIO_HAS_CSTDINT) +# if !defined(ASIO_DISABLE_CSTDINT) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_CSTDINT 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_CSTDINT 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_CSTDINT 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_CSTDINT 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CSTDINT) +#endif // !defined(ASIO_HAS_CSTDINT) + +// Standard library support for the thread class. +#if !defined(ASIO_HAS_STD_THREAD) +# if !defined(ASIO_DISABLE_STD_THREAD) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_THREAD 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_THREAD 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_THREAD 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_THREAD 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_THREAD) +#endif // !defined(ASIO_HAS_STD_THREAD) + +// Standard library support for the mutex and condition variable classes. +#if !defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# if !defined(ASIO_DISABLE_STD_MUTEX_AND_CONDVAR) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_MUTEX_AND_CONDVAR) +#endif // !defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +// Standard library support for the call_once function. +#if !defined(ASIO_HAS_STD_CALL_ONCE) +# if !defined(ASIO_DISABLE_STD_CALL_ONCE) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_CALL_ONCE 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_CALL_ONCE 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_CALL_ONCE 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_CALL_ONCE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_CALL_ONCE) +#endif // !defined(ASIO_HAS_STD_CALL_ONCE) + +// Standard library support for futures. +#if !defined(ASIO_HAS_STD_FUTURE) +# if !defined(ASIO_DISABLE_STD_FUTURE) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_FUTURE 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_FUTURE 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_FUTURE 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_FUTURE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_FUTURE) +#endif // !defined(ASIO_HAS_STD_FUTURE) + +// Standard library support for std::string_view. +#if !defined(ASIO_HAS_STD_STRING_VIEW) +# if !defined(ASIO_DISABLE_STD_STRING_VIEW) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# if (__cplusplus >= 201402) +# if __has_include() +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201402) +# else // defined(ASIO_HAS_CLANG_LIBCXX) +# if (__cplusplus >= 201703) +# if __has_include() +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201703) +# endif // defined(ASIO_HAS_CLANG_LIBCXX) +# elif defined(__GNUC__) +# if (__GNUC__ >= 7) +# if (__cplusplus >= 201703) +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // (__cplusplus >= 201703) +# endif // (__GNUC__ >= 7) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_STRING_VIEW) +#endif // !defined(ASIO_HAS_STD_STRING_VIEW) + +// Standard library support for std::experimental::string_view. +#if !defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# if !defined(ASIO_DISABLE_STD_EXPERIMENTAL_STRING_VIEW) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# if (_LIBCPP_VERSION < 7000) +# if (__cplusplus >= 201402) +# if __has_include() +# define ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201402) +# endif // (_LIBCPP_VERSION < 7000) +# else // defined(ASIO_HAS_CLANG_LIBCXX) +# if (__cplusplus >= 201402) +# if __has_include() +# define ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201402) +# endif // // defined(ASIO_HAS_CLANG_LIBCXX) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4) +# if (__cplusplus >= 201402) +# define ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW 1 +# endif // (__cplusplus >= 201402) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# endif // !defined(ASIO_DISABLE_STD_EXPERIMENTAL_STRING_VIEW) +#endif // !defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) + +// Standard library has a string_view that we can use. +#if !defined(ASIO_HAS_STRING_VIEW) +# if !defined(ASIO_DISABLE_STRING_VIEW) +# if defined(ASIO_HAS_STD_STRING_VIEW) +# define ASIO_HAS_STRING_VIEW 1 +# elif defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# define ASIO_HAS_STRING_VIEW 1 +# endif // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# endif // !defined(ASIO_DISABLE_STRING_VIEW) +#endif // !defined(ASIO_HAS_STRING_VIEW) + +// Standard library support for iostream move construction and assignment. +#if !defined(ASIO_HAS_STD_IOSTREAM_MOVE) +# if !defined(ASIO_DISABLE_STD_IOSTREAM_MOVE) +# if defined(__GNUC__) +# if (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_IOSTREAM_MOVE 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_IOSTREAM_MOVE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_IOSTREAM_MOVE) +#endif // !defined(ASIO_HAS_STD_IOSTREAM_MOVE) + +// Standard library has invoke_result (which supersedes result_of). +#if !defined(ASIO_HAS_STD_INVOKE_RESULT) +# if !defined(ASIO_DISABLE_STD_INVOKE_RESULT) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1911 && _MSVC_LANG >= 201703) +# define ASIO_HAS_STD_INVOKE_RESULT 1 +# endif // (_MSC_VER >= 1911 && _MSVC_LANG >= 201703) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_INVOKE_RESULT) +#endif // !defined(ASIO_HAS_STD_INVOKE_RESULT) + +// Windows App target. Windows but with a limited API. +#if !defined(ASIO_WINDOWS_APP) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0603) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) \ + && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define ASIO_WINDOWS_APP 1 +# endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + // && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0603) +#endif // !defined(ASIO_WINDOWS_APP) + +// Legacy WinRT target. Windows App is preferred. +#if !defined(ASIO_WINDOWS_RUNTIME) +# if !defined(ASIO_WINDOWS_APP) +# if defined(__cplusplus_winrt) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) \ + && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define ASIO_WINDOWS_RUNTIME 1 +# endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + // && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# endif // defined(__cplusplus_winrt) +# endif // !defined(ASIO_WINDOWS_APP) +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +// Windows target. Excludes WinRT but includes Windows App targets. +#if !defined(ASIO_WINDOWS) +# if !defined(ASIO_WINDOWS_RUNTIME) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_WINDOWS) +# define ASIO_WINDOWS 1 +# elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define ASIO_WINDOWS 1 +# elif defined(ASIO_WINDOWS_APP) +# define ASIO_WINDOWS 1 +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_WINDOWS) +# endif // !defined(ASIO_WINDOWS_RUNTIME) +#endif // !defined(ASIO_WINDOWS) + +// Windows: target OS version. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma message( \ + "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\ + "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\ + "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\ + "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).") +# else // defined(_MSC_VER) || defined(__BORLANDC__) +# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. +# warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line. +# warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target). +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# define _WIN32_WINNT 0x0501 +# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) +# if defined(_WIN32) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(_WIN32) && !defined(WIN32) +# endif // defined(_MSC_VER) +# if defined(__BORLANDC__) +# if defined(__WIN32__) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(__WIN32__) && !defined(WIN32) +# endif // defined(__BORLANDC__) +# if defined(__CYGWIN__) +# if !defined(__USE_W32_SOCKETS) +# error You must add -D__USE_W32_SOCKETS to your compiler options. +# endif // !defined(__USE_W32_SOCKETS) +# endif // defined(__CYGWIN__) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Windows: minimise header inclusion. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif // !defined(WIN32_LEAN_AND_MEAN) +# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Windows: suppress definition of "min" and "max" macros. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(ASIO_NO_NOMINMAX) +# if !defined(NOMINMAX) +# define NOMINMAX 1 +# endif // !defined(NOMINMAX) +# endif // !defined(ASIO_NO_NOMINMAX) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Windows: IO Completion Ports. +#if !defined(ASIO_HAS_IOCP) +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +# if !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# if !defined(ASIO_DISABLE_IOCP) +# define ASIO_HAS_IOCP 1 +# endif // !defined(ASIO_DISABLE_IOCP) +# endif // !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +#endif // !defined(ASIO_HAS_IOCP) + +// On POSIX (and POSIX-like) platforms we need to include unistd.h in order to +// get access to the various platform feature macros, e.g. to be able to test +// for threads support. +#if !defined(ASIO_HAS_UNISTD_H) +# if !defined(ASIO_HAS_BOOST_CONFIG) +# if defined(unix) \ + || defined(__unix) \ + || defined(_XOPEN_SOURCE) \ + || defined(_POSIX_SOURCE) \ + || (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__linux__) +# define ASIO_HAS_UNISTD_H 1 +# endif +# endif // !defined(ASIO_HAS_BOOST_CONFIG) +#endif // !defined(ASIO_HAS_UNISTD_H) +#if defined(ASIO_HAS_UNISTD_H) +# include +#endif // defined(ASIO_HAS_UNISTD_H) + +// Linux: epoll, eventfd and timerfd. +#if defined(__linux__) +# include +# if !defined(ASIO_HAS_EPOLL) +# if !defined(ASIO_DISABLE_EPOLL) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +# define ASIO_HAS_EPOLL 1 +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +# endif // !defined(ASIO_DISABLE_EPOLL) +# endif // !defined(ASIO_HAS_EPOLL) +# if !defined(ASIO_HAS_EVENTFD) +# if !defined(ASIO_DISABLE_EVENTFD) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# define ASIO_HAS_EVENTFD 1 +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# endif // !defined(ASIO_DISABLE_EVENTFD) +# endif // !defined(ASIO_HAS_EVENTFD) +# if !defined(ASIO_HAS_TIMERFD) +# if defined(ASIO_HAS_EPOLL) +# if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# define ASIO_HAS_TIMERFD 1 +# endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# endif // defined(ASIO_HAS_EPOLL) +# endif // !defined(ASIO_HAS_TIMERFD) +#endif // defined(__linux__) + +// Mac OS X, FreeBSD, NetBSD, OpenBSD: kqueue. +#if (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) +# if !defined(ASIO_HAS_KQUEUE) +# if !defined(ASIO_DISABLE_KQUEUE) +# define ASIO_HAS_KQUEUE 1 +# endif // !defined(ASIO_DISABLE_KQUEUE) +# endif // !defined(ASIO_HAS_KQUEUE) +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || defined(__FreeBSD__) + // || defined(__NetBSD__) + // || defined(__OpenBSD__) + +// Solaris: /dev/poll. +#if defined(__sun) +# if !defined(ASIO_HAS_DEV_POLL) +# if !defined(ASIO_DISABLE_DEV_POLL) +# define ASIO_HAS_DEV_POLL 1 +# endif // !defined(ASIO_DISABLE_DEV_POLL) +# endif // !defined(ASIO_HAS_DEV_POLL) +#endif // defined(__sun) + +// Serial ports. +#if !defined(ASIO_HAS_SERIAL_PORT) +# if defined(ASIO_HAS_IOCP) \ + || !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +# if !defined(__SYMBIAN32__) +# if !defined(ASIO_DISABLE_SERIAL_PORT) +# define ASIO_HAS_SERIAL_PORT 1 +# endif // !defined(ASIO_DISABLE_SERIAL_PORT) +# endif // !defined(__SYMBIAN32__) +# endif // defined(ASIO_HAS_IOCP) + // || !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +#endif // !defined(ASIO_HAS_SERIAL_PORT) + +// Windows: stream handles. +#if !defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) +# if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_STREAM_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +# endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) +#endif // !defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + +// Windows: random access handles. +#if !defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) +# if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +# endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) +#endif // !defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + +// Windows: object handles. +#if !defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) +# if !defined(ASIO_DISABLE_WINDOWS_OBJECT_HANDLE) +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# define ASIO_HAS_WINDOWS_OBJECT_HANDLE 1 +# endif // !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# endif // !defined(ASIO_DISABLE_WINDOWS_OBJECT_HANDLE) +#endif // !defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +// Windows: OVERLAPPED wrapper. +#if !defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) +# if !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_OVERLAPPED_PTR 1 +# endif // defined(ASIO_HAS_IOCP) +# endif // !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) +#endif // !defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) + +// POSIX: stream-oriented file descriptors. +#if !defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) +# if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) +# if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +# define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1 +# endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +# endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) +#endif // !defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + +// UNIX domain sockets. +#if !defined(ASIO_HAS_LOCAL_SOCKETS) +# if !defined(ASIO_DISABLE_LOCAL_SOCKETS) +# if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +# define ASIO_HAS_LOCAL_SOCKETS 1 +# endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +# endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS) +#endif // !defined(ASIO_HAS_LOCAL_SOCKETS) + +// Can use sigaction() instead of signal(). +#if !defined(ASIO_HAS_SIGACTION) +# if !defined(ASIO_DISABLE_SIGACTION) +# if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +# define ASIO_HAS_SIGACTION 1 +# endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +# endif // !defined(ASIO_DISABLE_SIGACTION) +#endif // !defined(ASIO_HAS_SIGACTION) + +// Can use signal(). +#if !defined(ASIO_HAS_SIGNAL) +# if !defined(ASIO_DISABLE_SIGNAL) +# if !defined(UNDER_CE) +# define ASIO_HAS_SIGNAL 1 +# endif // !defined(UNDER_CE) +# endif // !defined(ASIO_DISABLE_SIGNAL) +#endif // !defined(ASIO_HAS_SIGNAL) + +// Can use getaddrinfo() and getnameinfo(). +#if !defined(ASIO_HAS_GETADDRINFO) +# if !defined(ASIO_DISABLE_GETADDRINFO) +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) +# define ASIO_HAS_GETADDRINFO 1 +# elif defined(UNDER_CE) +# define ASIO_HAS_GETADDRINFO 1 +# endif // defined(UNDER_CE) +# elif defined(__MACH__) && defined(__APPLE__) +# if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +# if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +# define ASIO_HAS_GETADDRINFO 1 +# endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +# else // defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +# define ASIO_HAS_GETADDRINFO 1 +# endif // defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +# else // defined(__MACH__) && defined(__APPLE__) +# define ASIO_HAS_GETADDRINFO 1 +# endif // defined(__MACH__) && defined(__APPLE__) +# endif // !defined(ASIO_DISABLE_GETADDRINFO) +#endif // !defined(ASIO_HAS_GETADDRINFO) + +// Whether standard iostreams are disabled. +#if !defined(ASIO_NO_IOSTREAM) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_NO_IOSTREAM) +# define ASIO_NO_IOSTREAM 1 +# endif // !defined(BOOST_NO_IOSTREAM) +#endif // !defined(ASIO_NO_IOSTREAM) + +// Whether exception handling is disabled. +#if !defined(ASIO_NO_EXCEPTIONS) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_NO_EXCEPTIONS) +# define ASIO_NO_EXCEPTIONS 1 +# endif // !defined(BOOST_NO_EXCEPTIONS) +#endif // !defined(ASIO_NO_EXCEPTIONS) + +// Whether the typeid operator is supported. +#if !defined(ASIO_NO_TYPEID) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_NO_TYPEID) +# define ASIO_NO_TYPEID 1 +# endif // !defined(BOOST_NO_TYPEID) +#endif // !defined(ASIO_NO_TYPEID) + +// Threads. +#if !defined(ASIO_HAS_THREADS) +# if !defined(ASIO_DISABLE_THREADS) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_THREADS) +# define ASIO_HAS_THREADS 1 +# elif defined(__GNUC__) && !defined(__MINGW32__) \ + && !defined(linux) && !defined(__linux) && !defined(__linux__) +# define ASIO_HAS_THREADS 1 +# elif defined(_MT) || defined(__MT__) +# define ASIO_HAS_THREADS 1 +# elif defined(_REENTRANT) +# define ASIO_HAS_THREADS 1 +# elif defined(__APPLE__) +# define ASIO_HAS_THREADS 1 +# elif defined(_POSIX_THREADS) && (_POSIX_THREADS + 0 >= 0) +# define ASIO_HAS_THREADS 1 +# elif defined(_PTHREADS) +# define ASIO_HAS_THREADS 1 +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_THREADS) +# endif // !defined(ASIO_DISABLE_THREADS) +#endif // !defined(ASIO_HAS_THREADS) + +// POSIX threads. +#if !defined(ASIO_HAS_PTHREADS) +# if defined(ASIO_HAS_THREADS) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_PTHREADS) +# define ASIO_HAS_PTHREADS 1 +# elif defined(_POSIX_THREADS) && (_POSIX_THREADS + 0 >= 0) +# define ASIO_HAS_PTHREADS 1 +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_PTHREADS) +# endif // defined(ASIO_HAS_THREADS) +#endif // !defined(ASIO_HAS_PTHREADS) + +// Helper to prevent macro expansion. +#define ASIO_PREVENT_MACRO_SUBSTITUTION + +// Helper to define in-class constants. +#if !defined(ASIO_STATIC_CONSTANT) +# if !defined(ASIO_DISABLE_BOOST_STATIC_CONSTANT) +# define ASIO_STATIC_CONSTANT(type, assignment) \ + BOOST_STATIC_CONSTANT(type, assignment) +# else // !defined(ASIO_DISABLE_BOOST_STATIC_CONSTANT) +# define ASIO_STATIC_CONSTANT(type, assignment) \ + static const type assignment +# endif // !defined(ASIO_DISABLE_BOOST_STATIC_CONSTANT) +#endif // !defined(ASIO_STATIC_CONSTANT) + +// Boost array library. +#if !defined(ASIO_HAS_BOOST_ARRAY) +# if !defined(ASIO_DISABLE_BOOST_ARRAY) +# define ASIO_HAS_BOOST_ARRAY 1 +# endif // !defined(ASIO_DISABLE_BOOST_ARRAY) +#endif // !defined(ASIO_HAS_BOOST_ARRAY) + +// Boost assert macro. +#if !defined(ASIO_HAS_BOOST_ASSERT) +# if !defined(ASIO_DISABLE_BOOST_ASSERT) +# define ASIO_HAS_BOOST_ASSERT 1 +# endif // !defined(ASIO_DISABLE_BOOST_ASSERT) +#endif // !defined(ASIO_HAS_BOOST_ASSERT) + +// Boost limits header. +#if !defined(ASIO_HAS_BOOST_LIMITS) +# if !defined(ASIO_DISABLE_BOOST_LIMITS) +# define ASIO_HAS_BOOST_LIMITS 1 +# endif // !defined(ASIO_DISABLE_BOOST_LIMITS) +#endif // !defined(ASIO_HAS_BOOST_LIMITS) + +// Boost throw_exception function. +#if !defined(ASIO_HAS_BOOST_THROW_EXCEPTION) +# if !defined(ASIO_DISABLE_BOOST_THROW_EXCEPTION) +# define ASIO_HAS_BOOST_THROW_EXCEPTION 1 +# endif // !defined(ASIO_DISABLE_BOOST_THROW_EXCEPTION) +#endif // !defined(ASIO_HAS_BOOST_THROW_EXCEPTION) + +// Boost regex library. +#if !defined(ASIO_HAS_BOOST_REGEX) +# if !defined(ASIO_DISABLE_BOOST_REGEX) +# define ASIO_HAS_BOOST_REGEX 1 +# endif // !defined(ASIO_DISABLE_BOOST_REGEX) +#endif // !defined(ASIO_HAS_BOOST_REGEX) + +// Boost bind function. +#if !defined(ASIO_HAS_BOOST_BIND) +# if !defined(ASIO_DISABLE_BOOST_BIND) +# define ASIO_HAS_BOOST_BIND 1 +# endif // !defined(ASIO_DISABLE_BOOST_BIND) +#endif // !defined(ASIO_HAS_BOOST_BIND) + +// Boost's BOOST_WORKAROUND macro. +#if !defined(ASIO_HAS_BOOST_WORKAROUND) +# if !defined(ASIO_DISABLE_BOOST_WORKAROUND) +# define ASIO_HAS_BOOST_WORKAROUND 1 +# endif // !defined(ASIO_DISABLE_BOOST_WORKAROUND) +#endif // !defined(ASIO_HAS_BOOST_WORKAROUND) + +// Microsoft Visual C++'s secure C runtime library. +#if !defined(ASIO_HAS_SECURE_RTL) +# if !defined(ASIO_DISABLE_SECURE_RTL) +# if defined(ASIO_MSVC) \ + && (ASIO_MSVC >= 1400) \ + && !defined(UNDER_CE) +# define ASIO_HAS_SECURE_RTL 1 +# endif // defined(ASIO_MSVC) + // && (ASIO_MSVC >= 1400) + // && !defined(UNDER_CE) +# endif // !defined(ASIO_DISABLE_SECURE_RTL) +#endif // !defined(ASIO_HAS_SECURE_RTL) + +// Handler hooking. Disabled for ancient Borland C++ and gcc compilers. +#if !defined(ASIO_HAS_HANDLER_HOOKS) +# if !defined(ASIO_DISABLE_HANDLER_HOOKS) +# if defined(__GNUC__) +# if (__GNUC__ >= 3) +# define ASIO_HAS_HANDLER_HOOKS 1 +# endif // (__GNUC__ >= 3) +# elif !defined(__BORLANDC__) +# define ASIO_HAS_HANDLER_HOOKS 1 +# endif // !defined(__BORLANDC__) +# endif // !defined(ASIO_DISABLE_HANDLER_HOOKS) +#endif // !defined(ASIO_HAS_HANDLER_HOOKS) + +// Support for the __thread keyword extension. +#if !defined(ASIO_DISABLE_THREAD_KEYWORD_EXTENSION) +# if defined(__linux__) +# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# if ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3) +# if !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !(defined(__clang__) && defined(__ANDROID__)) +# define ASIO_HAS_THREAD_KEYWORD_EXTENSION 1 +# define ASIO_THREAD_KEYWORD __thread +# elif defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1100) +# define ASIO_HAS_THREAD_KEYWORD_EXTENSION 1 +# endif // defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1100) + // && !(defined(__clang__) && defined(__ANDROID__)) +# endif // ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3) +# endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# endif // defined(__linux__) +# if defined(ASIO_MSVC) && defined(ASIO_WINDOWS_RUNTIME) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_THREAD_KEYWORD_EXTENSION 1 +# define ASIO_THREAD_KEYWORD __declspec(thread) +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) && defined(ASIO_WINDOWS_RUNTIME) +#endif // !defined(ASIO_DISABLE_THREAD_KEYWORD_EXTENSION) +#if !defined(ASIO_THREAD_KEYWORD) +# define ASIO_THREAD_KEYWORD __thread +#endif // !defined(ASIO_THREAD_KEYWORD) + +// Support for POSIX ssize_t typedef. +#if !defined(ASIO_DISABLE_SSIZE_T) +# if defined(__linux__) \ + || (defined(__MACH__) && defined(__APPLE__)) +# define ASIO_HAS_SSIZE_T 1 +# endif // defined(__linux__) + // || (defined(__MACH__) && defined(__APPLE__)) +#endif // !defined(ASIO_DISABLE_SSIZE_T) + +// Helper macros to manage the transition away from the old services-based API. +#if defined(ASIO_ENABLE_OLD_SERVICES) +# define ASIO_SVC_TPARAM , typename Service +# define ASIO_SVC_TPARAM_DEF1(d1) , typename Service d1 +# define ASIO_SVC_TPARAM_DEF2(d1, d2) , typename Service d1, d2 +# define ASIO_SVC_TARG , Service +# define ASIO_SVC_T Service +# define ASIO_SVC_TPARAM1 , typename Service1 +# define ASIO_SVC_TPARAM1_DEF1(d1) , typename Service1 d1 +# define ASIO_SVC_TPARAM1_DEF2(d1, d2) , typename Service1 d1, d2 +# define ASIO_SVC_TARG1 , Service1 +# define ASIO_SVC_T1 Service1 +# define ASIO_SVC_ACCESS public +#else // defined(ASIO_ENABLE_OLD_SERVICES) +# define ASIO_SVC_TPARAM +# define ASIO_SVC_TPARAM_DEF1(d1) +# define ASIO_SVC_TPARAM_DEF2(d1, d2) +# define ASIO_SVC_TARG +// ASIO_SVC_T is defined at each point of use. +# define ASIO_SVC_TPARAM1 +# define ASIO_SVC_TPARAM1_DEF1(d1) +# define ASIO_SVC_TPARAM1_DEF2(d1, d2) +# define ASIO_SVC_TARG1 +// ASIO_SVC_T1 is defined at each point of use. +# define ASIO_SVC_ACCESS protected +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +// Helper macros to manage transition away from error_code return values. +#if defined(ASIO_NO_DEPRECATED) +# define ASIO_SYNC_OP_VOID void +# define ASIO_SYNC_OP_VOID_RETURN(e) return +#else // defined(ASIO_NO_DEPRECATED) +# define ASIO_SYNC_OP_VOID asio::error_code +# define ASIO_SYNC_OP_VOID_RETURN(e) return e +#endif // defined(ASIO_NO_DEPRECATED) + +// Newer gcc, clang need special treatment to suppress unused typedef warnings. +#if defined(__clang__) +# if defined(__apple_build_version__) +# if (__clang_major__ >= 7) +# define ASIO_UNUSED_TYPEDEF __attribute__((__unused__)) +# endif // (__clang_major__ >= 7) +# elif ((__clang_major__ == 3) && (__clang_minor__ >= 6)) \ + || (__clang_major__ > 3) +# define ASIO_UNUSED_TYPEDEF __attribute__((__unused__)) +# endif // ((__clang_major__ == 3) && (__clang_minor__ >= 6)) + // || (__clang_major__ > 3) +#elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# define ASIO_UNUSED_TYPEDEF __attribute__((__unused__)) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +#endif // defined(__GNUC__) +#if !defined(ASIO_UNUSED_TYPEDEF) +# define ASIO_UNUSED_TYPEDEF +#endif // !defined(ASIO_UNUSED_TYPEDEF) + +// Some versions of gcc generate spurious warnings about unused variables. +#if defined(__GNUC__) +# if (__GNUC__ >= 4) +# define ASIO_UNUSED_VARIABLE __attribute__((__unused__)) +# endif // (__GNUC__ >= 4) +#endif // defined(__GNUC__) +#if !defined(ASIO_UNUSED_VARIABLE) +# define ASIO_UNUSED_VARIABLE +#endif // !defined(ASIO_UNUSED_VARIABLE) + +// Support co_await on compilers known to allow it. +#if !defined(ASIO_HAS_CO_AWAIT) +# if !defined(ASIO_DISABLE_CO_AWAIT) +# if defined(ASIO_MSVC) +# if (_MSC_FULL_VER >= 190023506) +# if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) +# define ASIO_HAS_CO_AWAIT 1 +# endif // defined(_RESUMABLE_FUNCTIONS_SUPPORTED) +# endif // (_MSC_FULL_VER >= 190023506) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CO_AWAIT) +# if defined(__clang__) +# if (__cpp_coroutines >= 201703) +# if __has_include() +# define ASIO_HAS_CO_AWAIT 1 +# endif // __has_include() +# endif // (__cpp_coroutines >= 201703) +# endif // defined(__clang__) +#endif // !defined(ASIO_HAS_CO_AWAIT) + +#endif // ASIO_DETAIL_CONFIG_HPP diff --git a/subprojects/asio/include/asio/detail/consuming_buffers.hpp b/subprojects/asio/include/asio/detail/consuming_buffers.hpp new file mode 100644 index 000000000..8127ae75c --- /dev/null +++ b/subprojects/asio/include/asio/detail/consuming_buffers.hpp @@ -0,0 +1,414 @@ +// +// detail/consuming_buffers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP +#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/buffer.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/limits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper template to determine the maximum number of prepared buffers. +template +struct prepared_buffers_max +{ + enum { value = buffer_sequence_adapter_base::max_buffers }; +}; + +template +struct prepared_buffers_max > +{ + enum { value = N }; +}; + +#if defined(ASIO_HAS_STD_ARRAY) + +template +struct prepared_buffers_max > +{ + enum { value = N }; +}; + +#endif // defined(ASIO_HAS_STD_ARRAY) + +// A buffer sequence used to represent a subsequence of the buffers. +template +struct prepared_buffers +{ + typedef Buffer value_type; + typedef const Buffer* const_iterator; + + enum { max_buffers = MaxBuffers < 16 ? MaxBuffers : 16 }; + + prepared_buffers() : count(0) {} + const_iterator begin() const { return elems; } + const_iterator end() const { return elems + count; } + + Buffer elems[max_buffers]; + std::size_t count; +}; + +// A proxy for a sub-range in a list of buffers. +template +class consuming_buffers +{ +public: + typedef prepared_buffers::value> + prepared_buffers_type; + + // Construct to represent the entire list of buffers. + explicit consuming_buffers(const Buffers& buffers) + : buffers_(buffers), + total_consumed_(0), + next_elem_(0), + next_elem_offset_(0) + { + using asio::buffer_size; + total_size_ = buffer_size(buffers); + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= total_size_; + } + + // Get the buffer for a single transfer, with a size. + prepared_buffers_type prepare(std::size_t max_size) + { + prepared_buffers_type result; + + Buffer_Iterator next = asio::buffer_sequence_begin(buffers_); + Buffer_Iterator end = asio::buffer_sequence_end(buffers_); + + std::advance(next, next_elem_); + std::size_t elem_offset = next_elem_offset_; + while (next != end && max_size > 0 && (result.count) < result.max_buffers) + { + Buffer next_buf = Buffer(*next) + elem_offset; + result.elems[result.count] = asio::buffer(next_buf, max_size); + max_size -= result.elems[result.count].size(); + elem_offset = 0; + if (result.elems[result.count].size() > 0) + ++result.count; + ++next; + } + + return result; + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + + Buffer_Iterator next = asio::buffer_sequence_begin(buffers_); + Buffer_Iterator end = asio::buffer_sequence_end(buffers_); + + std::advance(next, next_elem_); + while (next != end && size > 0) + { + Buffer next_buf = Buffer(*next) + next_elem_offset_; + if (size < next_buf.size()) + { + next_elem_offset_ += size; + size = 0; + } + else + { + size -= next_buf.size(); + next_elem_offset_ = 0; + ++next_elem_; + ++next; + } + } + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + Buffers buffers_; + std::size_t total_size_; + std::size_t total_consumed_; + std::size_t next_elem_; + std::size_t next_elem_offset_; +}; + +// Base class of all consuming_buffers specialisations for single buffers. +template +class consuming_single_buffer +{ +public: + // Construct to represent the entire list of buffers. + template + explicit consuming_single_buffer(const Buffer1& buffer) + : buffer_(buffer), + total_consumed_(0) + { + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= buffer_.size(); + } + + // Get the buffer for a single transfer, with a size. + Buffer prepare(std::size_t max_size) + { + return asio::buffer(buffer_ + total_consumed_, max_size); + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + Buffer buffer_; + std::size_t total_consumed_; +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffer& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffer& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const const_buffer& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffers_1& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffers_1& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const const_buffers_1& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +class consuming_buffers, + typename boost::array::const_iterator> +{ +public: + // Construct to represent the entire list of buffers. + explicit consuming_buffers(const boost::array& buffers) + : buffers_(buffers), + total_consumed_(0) + { + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= + Buffer(buffers_[0]).size() + Buffer(buffers_[1]).size(); + } + + // Get the buffer for a single transfer, with a size. + boost::array prepare(std::size_t max_size) + { + boost::array result = {{ + Buffer(buffers_[0]), Buffer(buffers_[1]) }}; + std::size_t buffer0_size = result[0].size(); + result[0] = asio::buffer(result[0] + total_consumed_, max_size); + result[1] = asio::buffer( + result[1] + (total_consumed_ < buffer0_size + ? 0 : total_consumed_ - buffer0_size), + max_size - result[0].size()); + return result; + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + boost::array buffers_; + std::size_t total_consumed_; +}; + +#if defined(ASIO_HAS_STD_ARRAY) + +template +class consuming_buffers, + typename std::array::const_iterator> +{ +public: + // Construct to represent the entire list of buffers. + explicit consuming_buffers(const std::array& buffers) + : buffers_(buffers), + total_consumed_(0) + { + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= + Buffer(buffers_[0]).size() + Buffer(buffers_[1]).size(); + } + + // Get the buffer for a single transfer, with a size. + std::array prepare(std::size_t max_size) + { + std::array result = {{ + Buffer(buffers_[0]), Buffer(buffers_[1]) }}; + std::size_t buffer0_size = result[0].size(); + result[0] = asio::buffer(result[0] + total_consumed_, max_size); + result[1] = asio::buffer( + result[1] + (total_consumed_ < buffer0_size + ? 0 : total_consumed_ - buffer0_size), + max_size - result[0].size()); + return result; + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + std::array buffers_; + std::size_t total_consumed_; +}; + +#endif // defined(ASIO_HAS_STD_ARRAY) + +// Specialisation for null_buffers to ensure that the null_buffers type is +// always passed through to the underlying read or write operation. +template +class consuming_buffers + : public asio::null_buffers +{ +public: + consuming_buffers(const null_buffers&) + { + // No-op. + } + + bool empty() + { + return false; + } + + null_buffers prepare(std::size_t) + { + return null_buffers(); + } + + void consume(std::size_t) + { + // No-op. + } + + std::size_t total_consumed() const + { + return 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP diff --git a/subprojects/asio/include/asio/detail/cstddef.hpp b/subprojects/asio/include/asio/detail/cstddef.hpp new file mode 100644 index 000000000..3912da40d --- /dev/null +++ b/subprojects/asio/include/asio/detail/cstddef.hpp @@ -0,0 +1,31 @@ +// +// detail/cstddef.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CSTDDEF_HPP +#define ASIO_DETAIL_CSTDDEF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +namespace asio { + +#if defined(ASIO_HAS_NULLPTR) +using std::nullptr_t; +#else // defined(ASIO_HAS_NULLPTR) +struct nullptr_t {}; +#endif // defined(ASIO_HAS_NULLPTR) + +} // namespace asio + +#endif // ASIO_DETAIL_CSTDDEF_HPP diff --git a/subprojects/asio/include/asio/detail/cstdint.hpp b/subprojects/asio/include/asio/detail/cstdint.hpp new file mode 100644 index 000000000..62342b2d2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/cstdint.hpp @@ -0,0 +1,60 @@ +// +// detail/cstdint.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CSTDINT_HPP +#define ASIO_DETAIL_CSTDINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CSTDINT) +# include +#else // defined(ASIO_HAS_CSTDINT) +# include +#endif // defined(ASIO_HAS_CSTDINT) + +namespace asio { + +#if defined(ASIO_HAS_CSTDINT) +using std::int16_t; +using std::int_least16_t; +using std::uint16_t; +using std::uint_least16_t; +using std::int32_t; +using std::int_least32_t; +using std::uint32_t; +using std::uint_least32_t; +using std::int64_t; +using std::int_least64_t; +using std::uint64_t; +using std::uint_least64_t; +using std::uintmax_t; +#else // defined(ASIO_HAS_CSTDINT) +using boost::int16_t; +using boost::int_least16_t; +using boost::uint16_t; +using boost::uint_least16_t; +using boost::int32_t; +using boost::int_least32_t; +using boost::uint32_t; +using boost::uint_least32_t; +using boost::int64_t; +using boost::int_least64_t; +using boost::uint64_t; +using boost::uint_least64_t; +using boost::uintmax_t; +#endif // defined(ASIO_HAS_CSTDINT) + +} // namespace asio + +#endif // ASIO_DETAIL_CSTDINT_HPP diff --git a/subprojects/asio/include/asio/detail/date_time_fwd.hpp b/subprojects/asio/include/asio/detail/date_time_fwd.hpp new file mode 100644 index 000000000..a159562eb --- /dev/null +++ b/subprojects/asio/include/asio/detail/date_time_fwd.hpp @@ -0,0 +1,34 @@ +// +// detail/date_time_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DATE_TIME_FWD_HPP +#define ASIO_DETAIL_DATE_TIME_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace boost { +namespace date_time { + +template +class base_time; + +} // namespace date_time +namespace posix_time { + +class ptime; + +} // namespace posix_time +} // namespace boost + +#endif // ASIO_DETAIL_DATE_TIME_FWD_HPP diff --git a/subprojects/asio/include/asio/detail/deadline_timer_service.hpp b/subprojects/asio/include/asio/detail/deadline_timer_service.hpp new file mode 100644 index 000000000..f58a6e0e7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/deadline_timer_service.hpp @@ -0,0 +1,278 @@ +// +// detail/deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" +#include "asio/detail/timer_queue_ptime.hpp" +#include "asio/detail/timer_scheduler.hpp" +#include "asio/detail/wait_handler.hpp" +#include "asio/detail/wait_op.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include +# include +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class deadline_timer_service + : public service_base > +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // The implementation type of the timer. This type is dependent on the + // underlying implementation of the timer service. + struct implementation_type + : private asio::detail::noncopyable + { + time_type expiry; + bool might_have_pending_waits; + typename timer_queue::per_timer_data timer_data; + }; + + // Constructor. + deadline_timer_service(asio::io_context& io_context) + : service_base >(io_context), + scheduler_(asio::use_service(io_context)) + { + scheduler_.init_task(); + scheduler_.add_timer_queue(timer_queue_); + } + + // Destructor. + ~deadline_timer_service() + { + scheduler_.remove_timer_queue(timer_queue_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // Construct a new timer implementation. + void construct(implementation_type& impl) + { + impl.expiry = time_type(); + impl.might_have_pending_waits = false; + } + + // Destroy a timer implementation. + void destroy(implementation_type& impl) + { + asio::error_code ec; + cancel(impl, ec); + } + + // Move-construct a new serial port implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + scheduler_.move_timer(timer_queue_, impl.timer_data, other_impl.timer_data); + + impl.expiry = other_impl.expiry; + other_impl.expiry = time_type(); + + impl.might_have_pending_waits = other_impl.might_have_pending_waits; + other_impl.might_have_pending_waits = false; + } + + // Move-assign from another serial port implementation. + void move_assign(implementation_type& impl, + deadline_timer_service& other_service, + implementation_type& other_impl) + { + if (this != &other_service) + if (impl.might_have_pending_waits) + scheduler_.cancel_timer(timer_queue_, impl.timer_data); + + other_service.scheduler_.move_timer(other_service.timer_queue_, + impl.timer_data, other_impl.timer_data); + + impl.expiry = other_impl.expiry; + other_impl.expiry = time_type(); + + impl.might_have_pending_waits = other_impl.might_have_pending_waits; + other_impl.might_have_pending_waits = false; + } + + // Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl, asio::error_code& ec) + { + if (!impl.might_have_pending_waits) + { + ec = asio::error_code(); + return 0; + } + + ASIO_HANDLER_OPERATION((scheduler_.context(), + "deadline_timer", &impl, 0, "cancel")); + + std::size_t count = scheduler_.cancel_timer(timer_queue_, impl.timer_data); + impl.might_have_pending_waits = false; + ec = asio::error_code(); + return count; + } + + // Cancels one asynchronous wait operation associated with the timer. + std::size_t cancel_one(implementation_type& impl, + asio::error_code& ec) + { + if (!impl.might_have_pending_waits) + { + ec = asio::error_code(); + return 0; + } + + ASIO_HANDLER_OPERATION((scheduler_.context(), + "deadline_timer", &impl, 0, "cancel_one")); + + std::size_t count = scheduler_.cancel_timer( + timer_queue_, impl.timer_data, 1); + if (count == 0) + impl.might_have_pending_waits = false; + ec = asio::error_code(); + return count; + } + + // Get the expiry time for the timer as an absolute time. + time_type expiry(const implementation_type& impl) const + { + return impl.expiry; + } + + // Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return impl.expiry; + } + + // Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return Time_Traits::subtract(this->expiry(impl), Time_Traits::now()); + } + + // Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time, asio::error_code& ec) + { + std::size_t count = cancel(impl, ec); + impl.expiry = expiry_time; + ec = asio::error_code(); + return count; + } + + // Set the expiry time for the timer relative to now. + std::size_t expires_after(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return expires_at(impl, + Time_Traits::add(Time_Traits::now(), expiry_time), ec); + } + + // Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return expires_at(impl, + Time_Traits::add(Time_Traits::now(), expiry_time), ec); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl, asio::error_code& ec) + { + time_type now = Time_Traits::now(); + ec = asio::error_code(); + while (Time_Traits::less_than(now, impl.expiry) && !ec) + { + this->do_wait(Time_Traits::to_posix_duration( + Time_Traits::subtract(impl.expiry, now)), ec); + now = Time_Traits::now(); + } + } + + // Start an asynchronous wait on the timer. + template + void async_wait(implementation_type& impl, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef wait_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + impl.might_have_pending_waits = true; + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "deadline_timer", &impl, 0, "async_wait")); + + scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p); + p.v = p.p = 0; + } + +private: + // Helper function to wait given a duration type. The duration type should + // either be of type boost::posix_time::time_duration, or implement the + // required subset of its interface. + template + void do_wait(const Duration& timeout, asio::error_code& ec) + { +#if defined(ASIO_WINDOWS_RUNTIME) + std::this_thread::sleep_for( + std::chrono::seconds(timeout.total_seconds()) + + std::chrono::microseconds(timeout.total_microseconds())); + ec = asio::error_code(); +#else // defined(ASIO_WINDOWS_RUNTIME) + ::timeval tv; + tv.tv_sec = timeout.total_seconds(); + tv.tv_usec = timeout.total_microseconds() % 1000000; + socket_ops::select(0, 0, 0, 0, &tv, ec); +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + // The queue of timers. + timer_queue timer_queue_; + + // The object that schedules and executes timers. Usually a reactor. + timer_scheduler& scheduler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/dependent_type.hpp b/subprojects/asio/include/asio/detail/dependent_type.hpp new file mode 100644 index 000000000..85b41c893 --- /dev/null +++ b/subprojects/asio/include/asio/detail/dependent_type.hpp @@ -0,0 +1,36 @@ +// +// detail/dependent_type.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DEPENDENT_TYPE_HPP +#define ASIO_DETAIL_DEPENDENT_TYPE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct dependent_type +{ + typedef T type; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEPENDENT_TYPE_HPP diff --git a/subprojects/asio/include/asio/detail/descriptor_ops.hpp b/subprojects/asio/include/asio/detail/descriptor_ops.hpp new file mode 100644 index 000000000..9c0560aa2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/descriptor_ops.hpp @@ -0,0 +1,121 @@ +// +// detail/descriptor_ops.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_OPS_HPP +#define ASIO_DETAIL_DESCRIPTOR_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include +#include "asio/error.hpp" +#include "asio/error_code.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace descriptor_ops { + +// Descriptor state bits. +enum +{ + // The user wants a non-blocking descriptor. + user_set_non_blocking = 1, + + // The descriptor has been set non-blocking. + internal_non_blocking = 2, + + // Helper "state" used to determine whether the descriptor is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking, + + // The descriptor may have been dup()-ed. + possible_dup = 4 +}; + +typedef unsigned char state_type; + +template +inline ReturnType error_wrapper(ReturnType return_value, + asio::error_code& ec) +{ + ec = asio::error_code(errno, + asio::error::get_system_category()); + return return_value; +} + +ASIO_DECL int open(const char* path, int flags, + asio::error_code& ec); + +ASIO_DECL int close(int d, state_type& state, + asio::error_code& ec); + +ASIO_DECL bool set_user_non_blocking(int d, + state_type& state, bool value, asio::error_code& ec); + +ASIO_DECL bool set_internal_non_blocking(int d, + state_type& state, bool value, asio::error_code& ec); + +typedef iovec buf; + +ASIO_DECL std::size_t sync_read(int d, state_type state, buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec); + +ASIO_DECL bool non_blocking_read(int d, buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred); + +ASIO_DECL std::size_t sync_write(int d, state_type state, + const buf* bufs, std::size_t count, bool all_empty, + asio::error_code& ec); + +ASIO_DECL bool non_blocking_write(int d, + const buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred); + +ASIO_DECL int ioctl(int d, state_type& state, long cmd, + ioctl_arg_type* arg, asio::error_code& ec); + +ASIO_DECL int fcntl(int d, int cmd, asio::error_code& ec); + +ASIO_DECL int fcntl(int d, int cmd, + long arg, asio::error_code& ec); + +ASIO_DECL int poll_read(int d, + state_type state, asio::error_code& ec); + +ASIO_DECL int poll_write(int d, + state_type state, asio::error_code& ec); + +ASIO_DECL int poll_error(int d, + state_type state, asio::error_code& ec); + +} // namespace descriptor_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/descriptor_ops.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_OPS_HPP diff --git a/subprojects/asio/include/asio/detail/descriptor_read_op.hpp b/subprojects/asio/include/asio/detail/descriptor_read_op.hpp new file mode 100644 index 000000000..6db4bfb95 --- /dev/null +++ b/subprojects/asio/include/asio/detail/descriptor_read_op.hpp @@ -0,0 +1,128 @@ +// +// detail/descriptor_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP +#define ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class descriptor_read_op_base : public reactor_op +{ +public: + descriptor_read_op_base(int descriptor, + const MutableBufferSequence& buffers, func_type complete_func) + : reactor_op(&descriptor_read_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static status do_perform(reactor_op* base) + { + descriptor_read_op_base* o(static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + status result = descriptor_ops::non_blocking_read(o->descriptor_, + bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_) + ? done : not_done; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_read", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + int descriptor_; + MutableBufferSequence buffers_; +}; + +template +class descriptor_read_op + : public descriptor_read_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(descriptor_read_op); + + descriptor_read_op(int descriptor, + const MutableBufferSequence& buffers, Handler& handler) + : descriptor_read_op_base( + descriptor, buffers, &descriptor_read_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + descriptor_read_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP diff --git a/subprojects/asio/include/asio/detail/descriptor_write_op.hpp b/subprojects/asio/include/asio/detail/descriptor_write_op.hpp new file mode 100644 index 000000000..a9ec2a976 --- /dev/null +++ b/subprojects/asio/include/asio/detail/descriptor_write_op.hpp @@ -0,0 +1,128 @@ +// +// detail/descriptor_write_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP +#define ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class descriptor_write_op_base : public reactor_op +{ +public: + descriptor_write_op_base(int descriptor, + const ConstBufferSequence& buffers, func_type complete_func) + : reactor_op(&descriptor_write_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static status do_perform(reactor_op* base) + { + descriptor_write_op_base* o(static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + status result = descriptor_ops::non_blocking_write(o->descriptor_, + bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_) + ? done : not_done; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_write", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + int descriptor_; + ConstBufferSequence buffers_; +}; + +template +class descriptor_write_op + : public descriptor_write_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(descriptor_write_op); + + descriptor_write_op(int descriptor, + const ConstBufferSequence& buffers, Handler& handler) + : descriptor_write_op_base( + descriptor, buffers, &descriptor_write_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + descriptor_write_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP diff --git a/subprojects/asio/include/asio/detail/dev_poll_reactor.hpp b/subprojects/asio/include/asio/detail/dev_poll_reactor.hpp new file mode 100644 index 000000000..e9e4e29e0 --- /dev/null +++ b/subprojects/asio/include/asio/detail/dev_poll_reactor.hpp @@ -0,0 +1,218 @@ +// +// detail/dev_poll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DEV_POLL_REACTOR_HPP +#define ASIO_DETAIL_DEV_POLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include +#include +#include +#include "asio/detail/hash_map.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class dev_poll_reactor + : public execution_context_service_base +{ +public: + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor data. + struct per_descriptor_data + { + }; + + // Constructor. + ASIO_DECL dev_poll_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~dev_poll_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, + bool is_continuation, bool allow_speculative); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data&, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data&); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data(per_descriptor_data&); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run /dev/poll once until interrupted or events are ready to be dispatched. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the select loop. + ASIO_DECL void interrupt(); + +private: + // Create the /dev/poll file descriptor. Throws an exception if the descriptor + // cannot be created. + ASIO_DECL static int do_dev_poll_create(); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Get the timeout value for the /dev/poll DP_POLL operation. The timeout + // value is returned as a number of milliseconds. A return value of -1 + // indicates that the poll should block indefinitely. + ASIO_DECL int get_timeout(int msec); + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the dev_poll_reactor's mutex. + ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec); + + // Add a pending event entry for the given descriptor. + ASIO_DECL ::pollfd& add_pending_event_change(int descriptor); + + // The scheduler implementation used to post completions. + scheduler& scheduler_; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The /dev/poll file descriptor. + int dev_poll_fd_; + + // Vector of /dev/poll events waiting to be written to the descriptor. + std::vector< ::pollfd> pending_event_changes_; + + // Hash map to associate a descriptor with a pending event change index. + hash_map pending_event_change_index_; + + // The interrupter is used to break a blocking DP_POLL operation. + select_interrupter interrupter_; + + // The queues of read, write and except operations. + reactor_op_queue op_queue_[max_ops]; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/dev_poll_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/dev_poll_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_DEV_POLL_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/epoll_reactor.hpp b/subprojects/asio/include/asio/detail/epoll_reactor.hpp new file mode 100644 index 000000000..5f5810993 --- /dev/null +++ b/subprojects/asio/include/asio/detail/epoll_reactor.hpp @@ -0,0 +1,266 @@ +// +// detail/epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/conditionally_enabled_mutex.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/object_pool.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_TIMERFD) +# include +#endif // defined(ASIO_HAS_TIMERFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class epoll_reactor + : public execution_context_service_base +{ +private: + // The mutex type used by this reactor. + typedef conditionally_enabled_mutex mutex; + +public: + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + class descriptor_state : operation + { + friend class epoll_reactor; + friend class object_pool_access; + + descriptor_state* next_; + descriptor_state* prev_; + + mutex mutex_; + epoll_reactor* reactor_; + int descriptor_; + uint32_t registered_events_; + op_queue op_queue_[max_ops]; + bool try_speculative_[max_ops]; + bool shutdown_; + + ASIO_DECL descriptor_state(bool locking); + void set_ready_events(uint32_t events) { task_result_ = events; } + void add_ready_events(uint32_t events) { task_result_ |= events; } + ASIO_DECL operation* perform_io(uint32_t events); + ASIO_DECL static void do_complete( + void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred); + }; + + // Per-descriptor data. + typedef descriptor_state* per_descriptor_data; + + // Constructor. + ASIO_DECL epoll_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~epoll_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data& descriptor_data); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data( + per_descriptor_data& descriptor_data); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run epoll once until interrupted or events are ready to be dispatched. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the select loop. + ASIO_DECL void interrupt(); + +private: + // The hint to pass to epoll_create to size its data structures. + enum { epoll_size = 20000 }; + + // Create the epoll file descriptor. Throws an exception if the descriptor + // cannot be created. + ASIO_DECL static int do_epoll_create(); + + // Create the timerfd file descriptor. Does not throw. + ASIO_DECL static int do_timerfd_create(); + + // Allocate a new descriptor state object. + ASIO_DECL descriptor_state* allocate_descriptor_state(); + + // Free an existing descriptor state object. + ASIO_DECL void free_descriptor_state(descriptor_state* s); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Called to recalculate and update the timeout. + ASIO_DECL void update_timeout(); + + // Get the timeout value for the epoll_wait call. The timeout value is + // returned as a number of milliseconds. A return value of -1 indicates + // that epoll_wait should block indefinitely. + ASIO_DECL int get_timeout(int msec); + +#if defined(ASIO_HAS_TIMERFD) + // Get the timeout value for the timer descriptor. The return value is the + // flag argument to be used when calling timerfd_settime. + ASIO_DECL int get_timeout(itimerspec& ts); +#endif // defined(ASIO_HAS_TIMERFD) + + // The scheduler implementation used to post completions. + scheduler& scheduler_; + + // Mutex to protect access to internal data. + mutex mutex_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The epoll file descriptor. + int epoll_fd_; + + // The timer file descriptor. + int timer_fd_; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; + + // Mutex to protect access to the registered descriptors. + mutex registered_descriptors_mutex_; + + // Keep track of all registered descriptors. + object_pool registered_descriptors_; + + // Helper class to do post-perform_io cleanup. + struct perform_io_cleanup_on_block_exit; + friend struct perform_io_cleanup_on_block_exit; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/epoll_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/epoll_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/event.hpp b/subprojects/asio/include/asio/detail/event.hpp new file mode 100644 index 000000000..da8fa770b --- /dev/null +++ b/subprojects/asio/include/asio/detail/event.hpp @@ -0,0 +1,48 @@ +// +// detail/event.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EVENT_HPP +#define ASIO_DETAIL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_event.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_event.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_event.hpp" +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# include "asio/detail/std_event.hpp" +#else +# error Only Windows, POSIX and std::condition_variable are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_event event; +#elif defined(ASIO_WINDOWS) +typedef win_event event; +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_event event; +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +typedef std_event event; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_EVENT_HPP diff --git a/subprojects/asio/include/asio/detail/eventfd_select_interrupter.hpp b/subprojects/asio/include/asio/detail/eventfd_select_interrupter.hpp new file mode 100644 index 000000000..f6e594b61 --- /dev/null +++ b/subprojects/asio/include/asio/detail/eventfd_select_interrupter.hpp @@ -0,0 +1,83 @@ +// +// detail/eventfd_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EVENTFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class eventfd_select_interrupter +{ +public: + // Constructor. + ASIO_DECL eventfd_select_interrupter(); + + // Destructor. + ASIO_DECL ~eventfd_select_interrupter(); + + // Recreate the interrupter's descriptors. Used after a fork. + ASIO_DECL void recreate(); + + // Interrupt the select call. + ASIO_DECL void interrupt(); + + // Reset the select interrupt. Returns true if the call was interrupted. + ASIO_DECL bool reset(); + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // Open the descriptors. Throws on error. + ASIO_DECL void open_descriptors(); + + // Close the descriptors. + ASIO_DECL void close_descriptors(); + + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // 64bit value will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // 64bit non-zero value may be written to this to wake up the select which is + // waiting for the other end to become readable. This descriptor will only + // differ from the read descriptor when a pipe is used. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/eventfd_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_EVENTFD) + +#endif // ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP diff --git a/subprojects/asio/include/asio/detail/executor_op.hpp b/subprojects/asio/include/asio/detail/executor_op.hpp new file mode 100644 index 000000000..2d5c7e8b8 --- /dev/null +++ b/subprojects/asio/include/asio/detail/executor_op.hpp @@ -0,0 +1,84 @@ +// +// detail/executor_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EXECUTOR_OP_HPP +#define ASIO_DETAIL_EXECUTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/scheduler_operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class executor_op : public Operation +{ +public: + ASIO_DEFINE_HANDLER_ALLOCATOR_PTR(executor_op); + + template + executor_op(ASIO_MOVE_ARG(H) h, const Alloc& allocator) + : Operation(&executor_op::do_complete), + handler_(ASIO_MOVE_CAST(H)(h)), + allocator_(allocator) + { + } + + static void do_complete(void* owner, Operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + executor_op* o(static_cast(base)); + Alloc allocator(o->allocator_); + ptr p = { detail::addressof(allocator), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + Handler handler(ASIO_MOVE_CAST(Handler)(o->handler_)); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN(()); + asio_handler_invoke_helpers::invoke(handler, handler); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + Alloc allocator_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EXECUTOR_OP_HPP diff --git a/subprojects/asio/include/asio/detail/fd_set_adapter.hpp b/subprojects/asio/include/asio/detail/fd_set_adapter.hpp new file mode 100644 index 000000000..fd373dae1 --- /dev/null +++ b/subprojects/asio/include/asio/detail/fd_set_adapter.hpp @@ -0,0 +1,39 @@ +// +// detail/fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/posix_fd_set_adapter.hpp" +#include "asio/detail/win_fd_set_adapter.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef win_fd_set_adapter fd_set_adapter; +#else +typedef posix_fd_set_adapter fd_set_adapter; +#endif + +} // namespace detail +} // namespace asio + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP diff --git a/subprojects/asio/include/asio/detail/fenced_block.hpp b/subprojects/asio/include/asio/detail/fenced_block.hpp new file mode 100644 index 000000000..dc34bd9fb --- /dev/null +++ b/subprojects/asio/include/asio/detail/fenced_block.hpp @@ -0,0 +1,80 @@ +// +// detail/fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FENCED_BLOCK_HPP +#define ASIO_DETAIL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) \ + || defined(ASIO_DISABLE_FENCED_BLOCK) +# include "asio/detail/null_fenced_block.hpp" +#elif defined(ASIO_HAS_STD_ATOMIC) +# include "asio/detail/std_fenced_block.hpp" +#elif defined(__MACH__) && defined(__APPLE__) +# include "asio/detail/macos_fenced_block.hpp" +#elif defined(__sun) +# include "asio/detail/solaris_fenced_block.hpp" +#elif defined(__GNUC__) && defined(__arm__) \ + && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) +# include "asio/detail/gcc_arm_fenced_block.hpp" +#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) +# include "asio/detail/gcc_hppa_fenced_block.hpp" +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# include "asio/detail/gcc_x86_fenced_block.hpp" +#elif defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) +# include "asio/detail/gcc_sync_fenced_block.hpp" +#elif defined(ASIO_WINDOWS) && !defined(UNDER_CE) +# include "asio/detail/win_fenced_block.hpp" +#else +# include "asio/detail/null_fenced_block.hpp" +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) \ + || defined(ASIO_DISABLE_FENCED_BLOCK) +typedef null_fenced_block fenced_block; +#elif defined(ASIO_HAS_STD_ATOMIC) +typedef std_fenced_block fenced_block; +#elif defined(__MACH__) && defined(__APPLE__) +typedef macos_fenced_block fenced_block; +#elif defined(__sun) +typedef solaris_fenced_block fenced_block; +#elif defined(__GNUC__) && defined(__arm__) \ + && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) +typedef gcc_arm_fenced_block fenced_block; +#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) +typedef gcc_hppa_fenced_block fenced_block; +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +typedef gcc_x86_fenced_block fenced_block; +#elif defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) +typedef gcc_sync_fenced_block fenced_block; +#elif defined(ASIO_WINDOWS) && !defined(UNDER_CE) +typedef win_fenced_block fenced_block; +#else +typedef null_fenced_block fenced_block; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/functional.hpp b/subprojects/asio/include/asio/detail/functional.hpp new file mode 100644 index 000000000..a37e9e631 --- /dev/null +++ b/subprojects/asio/include/asio/detail/functional.hpp @@ -0,0 +1,38 @@ +// +// detail/functional.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FUNCTIONAL_HPP +#define ASIO_DETAIL_FUNCTIONAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include + +#if !defined(ASIO_HAS_STD_FUNCTION) +# include +#endif // !defined(ASIO_HAS_STD_FUNCTION) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_FUNCTION) +using std::function; +#else // defined(ASIO_HAS_STD_FUNCTION) +using boost::function; +#endif // defined(ASIO_HAS_STD_FUNCTION) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_FUNCTIONAL_HPP diff --git a/subprojects/asio/include/asio/detail/future.hpp b/subprojects/asio/include/asio/detail/future.hpp new file mode 100644 index 000000000..27461553f --- /dev/null +++ b/subprojects/asio/include/asio/detail/future.hpp @@ -0,0 +1,33 @@ +// +// detail/future.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FUTURE_HPP +#define ASIO_DETAIL_FUTURE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#if defined(ASIO_HAS_STD_FUTURE) +# include +// Even though the future header is available, libstdc++ may not implement the +// std::future class itself. However, we need to have already included the +// future header to reliably test for _GLIBCXX_HAS_GTHREADS. +# if defined(__GNUC__) && !defined(ASIO_HAS_CLANG_LIBCXX) +# if defined(_GLIBCXX_HAS_GTHREADS) +# define ASIO_HAS_STD_FUTURE_CLASS 1 +# endif // defined(_GLIBCXX_HAS_GTHREADS) +# else // defined(__GNUC__) && !defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_FUTURE_CLASS 1 +# endif // defined(__GNUC__) && !defined(ASIO_HAS_CLANG_LIBCXX) +#endif // defined(ASIO_HAS_STD_FUTURE) + +#endif // ASIO_DETAIL_FUTURE_HPP diff --git a/subprojects/asio/include/asio/detail/gcc_arm_fenced_block.hpp b/subprojects/asio/include/asio/detail/gcc_arm_fenced_block.hpp new file mode 100644 index 000000000..7919a551c --- /dev/null +++ b/subprojects/asio/include/asio/detail/gcc_arm_fenced_block.hpp @@ -0,0 +1,91 @@ +// +// detail/gcc_arm_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && defined(__arm__) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_arm_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit gcc_arm_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit gcc_arm_fenced_block(full_t) + { + barrier(); + } + + // Destructor. + ~gcc_arm_fenced_block() + { + barrier(); + } + +private: + static void barrier() + { +#if defined(__ARM_ARCH_4__) \ + || defined(__ARM_ARCH_4T__) \ + || defined(__ARM_ARCH_5__) \ + || defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) \ + || defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) +# if defined(__thumb__) + // This is just a placeholder and almost certainly not sufficient. + __asm__ __volatile__ ("" : : : "memory"); +# else // defined(__thumb__) + int a = 0, b = 0; + __asm__ __volatile__ ("swp %0, %1, [%2]" + : "=&r"(a) : "r"(1), "r"(&b) : "memory", "cc"); +# endif // defined(__thumb__) +#else + // ARMv7 and later. + __asm__ __volatile__ ("dmb" : : : "memory"); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && defined(__arm__) + +#endif // ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/gcc_hppa_fenced_block.hpp b/subprojects/asio/include/asio/detail/gcc_hppa_fenced_block.hpp new file mode 100644 index 000000000..d3957ce33 --- /dev/null +++ b/subprojects/asio/include/asio/detail/gcc_hppa_fenced_block.hpp @@ -0,0 +1,68 @@ +// +// detail/gcc_hppa_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_hppa_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit gcc_hppa_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit gcc_hppa_fenced_block(full_t) + { + barrier(); + } + + // Destructor. + ~gcc_hppa_fenced_block() + { + barrier(); + } + +private: + static void barrier() + { + // This is just a placeholder and almost certainly not sufficient. + __asm__ __volatile__ ("" : : : "memory"); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) + +#endif // ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/gcc_sync_fenced_block.hpp b/subprojects/asio/include/asio/detail/gcc_sync_fenced_block.hpp new file mode 100644 index 000000000..90d176f12 --- /dev/null +++ b/subprojects/asio/include/asio/detail/gcc_sync_fenced_block.hpp @@ -0,0 +1,65 @@ +// +// detail/gcc_sync_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_sync_fenced_block + : private noncopyable +{ +public: + enum half_or_full_t { half, full }; + + // Constructor. + explicit gcc_sync_fenced_block(half_or_full_t) + : value_(0) + { + __sync_lock_test_and_set(&value_, 1); + } + + // Destructor. + ~gcc_sync_fenced_block() + { + __sync_lock_release(&value_); + } + +private: + int value_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) + // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) + // && !defined(__INTEL_COMPILER) && !defined(__ICL) + // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#endif // ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/gcc_x86_fenced_block.hpp b/subprojects/asio/include/asio/detail/gcc_x86_fenced_block.hpp new file mode 100644 index 000000000..1366def74 --- /dev/null +++ b/subprojects/asio/include/asio/detail/gcc_x86_fenced_block.hpp @@ -0,0 +1,99 @@ +// +// detail/gcc_x86_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_x86_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit gcc_x86_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit gcc_x86_fenced_block(full_t) + { + lbarrier(); + } + + // Destructor. + ~gcc_x86_fenced_block() + { + sbarrier(); + } + +private: + static int barrier() + { + int r = 0, m = 1; + __asm__ __volatile__ ( + "xchgl %0, %1" : + "=r"(r), "=m"(m) : + "0"(1), "m"(m) : + "memory", "cc"); + return r; + } + + static void lbarrier() + { +#if defined(__SSE2__) +# if (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __builtin_ia32_lfence(); +# else // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __asm__ __volatile__ ("lfence" ::: "memory"); +# endif // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) +#else // defined(__SSE2__) + barrier(); +#endif // defined(__SSE2__) + } + + static void sbarrier() + { +#if defined(__SSE2__) +# if (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __builtin_ia32_sfence(); +# else // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __asm__ __volatile__ ("sfence" ::: "memory"); +# endif // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) +#else // defined(__SSE2__) + barrier(); +#endif // defined(__SSE2__) + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#endif // ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/global.hpp b/subprojects/asio/include/asio/detail/global.hpp new file mode 100644 index 000000000..085ac643d --- /dev/null +++ b/subprojects/asio/include/asio/detail/global.hpp @@ -0,0 +1,52 @@ +// +// detail/global.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GLOBAL_HPP +#define ASIO_DETAIL_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_global.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_global.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_global.hpp" +#elif defined(ASIO_HAS_STD_CALL_ONCE) +# include "asio/detail/std_global.hpp" +#else +# error Only Windows, POSIX and std::call_once are supported! +#endif + +namespace asio { +namespace detail { + +template +inline T& global() +{ +#if !defined(ASIO_HAS_THREADS) + return null_global(); +#elif defined(ASIO_WINDOWS) + return win_global(); +#elif defined(ASIO_HAS_PTHREADS) + return posix_global(); +#elif defined(ASIO_HAS_STD_CALL_ONCE) + return std_global(); +#endif +} + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_GLOBAL_HPP diff --git a/subprojects/asio/include/asio/detail/handler_alloc_helpers.hpp b/subprojects/asio/include/asio/detail/handler_alloc_helpers.hpp new file mode 100644 index 000000000..afefb4d12 --- /dev/null +++ b/subprojects/asio/include/asio/detail/handler_alloc_helpers.hpp @@ -0,0 +1,235 @@ +// +// detail/handler_alloc_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/handler_alloc_hook.hpp" + +#include "asio/detail/push_options.hpp" + +// Calls to asio_handler_allocate and asio_handler_deallocate must be made from +// a namespace that does not contain any overloads of these functions. The +// asio_handler_alloc_helpers namespace is defined here for that purpose. +namespace asio_handler_alloc_helpers { + +template +inline void* allocate(std::size_t s, Handler& h) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + return ::operator new(s); +#else + using asio::asio_handler_allocate; + return asio_handler_allocate(s, asio::detail::addressof(h)); +#endif +} + +template +inline void deallocate(void* p, std::size_t s, Handler& h) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + ::operator delete(p); +#else + using asio::asio_handler_deallocate; + asio_handler_deallocate(p, s, asio::detail::addressof(h)); +#endif +} + +} // namespace asio_handler_alloc_helpers + +namespace asio { +namespace detail { + +template +class hook_allocator +{ +public: + typedef T value_type; + + template + struct rebind + { + typedef hook_allocator other; + }; + + explicit hook_allocator(Handler& h) + : handler_(h) + { + } + + template + hook_allocator(const hook_allocator& a) + : handler_(a.handler_) + { + } + + T* allocate(std::size_t n) + { + return static_cast( + asio_handler_alloc_helpers::allocate(sizeof(T) * n, handler_)); + } + + void deallocate(T* p, std::size_t n) + { + asio_handler_alloc_helpers::deallocate(p, sizeof(T) * n, handler_); + } + +//private: + Handler& handler_; +}; + +template +class hook_allocator +{ +public: + typedef void value_type; + + template + struct rebind + { + typedef hook_allocator other; + }; + + explicit hook_allocator(Handler& h) + : handler_(h) + { + } + + template + hook_allocator(const hook_allocator& a) + : handler_(a.handler_) + { + } + +//private: + Handler& handler_; +}; + +template +struct get_hook_allocator +{ + typedef Allocator type; + + static type get(Handler&, const Allocator& a) + { + return a; + } +}; + +template +struct get_hook_allocator > +{ + typedef hook_allocator type; + + static type get(Handler& handler, const std::allocator&) + { + return type(handler); + } +}; + +} // namespace detail +} // namespace asio + +#define ASIO_DEFINE_HANDLER_PTR(op) \ + struct ptr \ + { \ + Handler* h; \ + op* v; \ + op* p; \ + ~ptr() \ + { \ + reset(); \ + } \ + static op* allocate(Handler& handler) \ + { \ + typedef typename ::asio::associated_allocator< \ + Handler>::type associated_allocator_type; \ + typedef typename ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::type hook_allocator_type; \ + ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \ + ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::get( \ + handler, ::asio::get_associated_allocator(handler))); \ + return a.allocate(1); \ + } \ + void reset() \ + { \ + if (p) \ + { \ + p->~op(); \ + p = 0; \ + } \ + if (v) \ + { \ + typedef typename ::asio::associated_allocator< \ + Handler>::type associated_allocator_type; \ + typedef typename ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::type hook_allocator_type; \ + ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \ + ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::get( \ + *h, ::asio::get_associated_allocator(*h))); \ + a.deallocate(static_cast(v), 1); \ + v = 0; \ + } \ + } \ + } \ + /**/ + +#define ASIO_DEFINE_HANDLER_ALLOCATOR_PTR(op) \ + struct ptr \ + { \ + const Alloc* a; \ + void* v; \ + op* p; \ + ~ptr() \ + { \ + reset(); \ + } \ + static op* allocate(const Alloc& a) \ + { \ + typedef typename ::asio::detail::get_recycling_allocator< \ + Alloc>::type recycling_allocator_type; \ + ASIO_REBIND_ALLOC(recycling_allocator_type, op) a1( \ + ::asio::detail::get_recycling_allocator::get(a)); \ + return a1.allocate(1); \ + } \ + void reset() \ + { \ + if (p) \ + { \ + p->~op(); \ + p = 0; \ + } \ + if (v) \ + { \ + typedef typename ::asio::detail::get_recycling_allocator< \ + Alloc>::type recycling_allocator_type; \ + ASIO_REBIND_ALLOC(recycling_allocator_type, op) a1( \ + ::asio::detail::get_recycling_allocator::get(*a)); \ + a1.deallocate(static_cast(v), 1); \ + v = 0; \ + } \ + } \ + } \ + /**/ + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP diff --git a/subprojects/asio/include/asio/detail/handler_cont_helpers.hpp b/subprojects/asio/include/asio/detail/handler_cont_helpers.hpp new file mode 100644 index 000000000..110ba944f --- /dev/null +++ b/subprojects/asio/include/asio/detail/handler_cont_helpers.hpp @@ -0,0 +1,45 @@ +// +// detail/handler_cont_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_CONT_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_CONT_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/handler_continuation_hook.hpp" + +#include "asio/detail/push_options.hpp" + +// Calls to asio_handler_is_continuation must be made from a namespace that +// does not contain overloads of this function. This namespace is defined here +// for that purpose. +namespace asio_handler_cont_helpers { + +template +inline bool is_continuation(Context& context) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + return false; +#else + using asio::asio_handler_is_continuation; + return asio_handler_is_continuation( + asio::detail::addressof(context)); +#endif +} + +} // namespace asio_handler_cont_helpers + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_CONT_HELPERS_HPP diff --git a/subprojects/asio/include/asio/detail/handler_invoke_helpers.hpp b/subprojects/asio/include/asio/detail/handler_invoke_helpers.hpp new file mode 100644 index 000000000..4c65c4c53 --- /dev/null +++ b/subprojects/asio/include/asio/detail/handler_invoke_helpers.hpp @@ -0,0 +1,57 @@ +// +// detail/handler_invoke_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/handler_invoke_hook.hpp" + +#include "asio/detail/push_options.hpp" + +// Calls to asio_handler_invoke must be made from a namespace that does not +// contain overloads of this function. The asio_handler_invoke_helpers +// namespace is defined here for that purpose. +namespace asio_handler_invoke_helpers { + +template +inline void invoke(Function& function, Context& context) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + Function tmp(function); + tmp(); +#else + using asio::asio_handler_invoke; + asio_handler_invoke(function, asio::detail::addressof(context)); +#endif +} + +template +inline void invoke(const Function& function, Context& context) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + Function tmp(function); + tmp(); +#else + using asio::asio_handler_invoke; + asio_handler_invoke(function, asio::detail::addressof(context)); +#endif +} + +} // namespace asio_handler_invoke_helpers + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP diff --git a/subprojects/asio/include/asio/detail/handler_tracking.hpp b/subprojects/asio/include/asio/detail/handler_tracking.hpp new file mode 100644 index 000000000..83f820eca --- /dev/null +++ b/subprojects/asio/include/asio/detail/handler_tracking.hpp @@ -0,0 +1,238 @@ +// +// detail/handler_tracking.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_TRACKING_HPP +#define ASIO_DETAIL_HANDLER_TRACKING_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace asio { + +class execution_context; + +} // namespace asio + +#if defined(ASIO_CUSTOM_HANDLER_TRACKING) +# include ASIO_CUSTOM_HANDLER_TRACKING +#elif defined(ASIO_ENABLE_HANDLER_TRACKING) +# include "asio/error_code.hpp" +# include "asio/detail/cstdint.hpp" +# include "asio/detail/static_mutex.hpp" +# include "asio/detail/tss_ptr.hpp" +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_CUSTOM_HANDLER_TRACKING) + +// The user-specified header must define the following macros: +// - ASIO_INHERIT_TRACKED_HANDLER +// - ASIO_ALSO_INHERIT_TRACKED_HANDLER +// - ASIO_HANDLER_TRACKING_INIT +// - ASIO_HANDLER_CREATION(args) +// - ASIO_HANDLER_COMPLETION(args) +// - ASIO_HANDLER_INVOCATION_BEGIN(args) +// - ASIO_HANDLER_INVOCATION_END +// - ASIO_HANDLER_OPERATION(args) +// - ASIO_HANDLER_REACTOR_REGISTRATION(args) +// - ASIO_HANDLER_REACTOR_DEREGISTRATION(args) +// - ASIO_HANDLER_REACTOR_READ_EVENT +// - ASIO_HANDLER_REACTOR_WRITE_EVENT +// - ASIO_HANDLER_REACTOR_ERROR_EVENT +// - ASIO_HANDLER_REACTOR_EVENTS(args) +// - ASIO_HANDLER_REACTOR_OPERATION(args) + +# if !defined(ASIO_ENABLE_HANDLER_TRACKING) +# define ASIO_ENABLE_HANDLER_TRACKING 1 +# endif /// !defined(ASIO_ENABLE_HANDLER_TRACKING) + +#elif defined(ASIO_ENABLE_HANDLER_TRACKING) + +class handler_tracking +{ +public: + class completion; + + // Base class for objects containing tracked handlers. + class tracked_handler + { + private: + // Only the handler_tracking class will have access to the id. + friend class handler_tracking; + friend class completion; + uint64_t id_; + + protected: + // Constructor initialises with no id. + tracked_handler() : id_(0) {} + + // Prevent deletion through this type. + ~tracked_handler() {} + }; + + // Initialise the tracking system. + ASIO_DECL static void init(); + + // Record the creation of a tracked handler. + ASIO_DECL static void creation( + execution_context& context, tracked_handler& h, + const char* object_type, void* object, + uintmax_t native_handle, const char* op_name); + + class completion + { + public: + // Constructor records that handler is to be invoked with no arguments. + ASIO_DECL explicit completion(const tracked_handler& h); + + // Destructor records only when an exception is thrown from the handler, or + // if the memory is being freed without the handler having been invoked. + ASIO_DECL ~completion(); + + // Records that handler is to be invoked with no arguments. + ASIO_DECL void invocation_begin(); + + // Records that handler is to be invoked with one arguments. + ASIO_DECL void invocation_begin(const asio::error_code& ec); + + // Constructor records that handler is to be invoked with two arguments. + ASIO_DECL void invocation_begin( + const asio::error_code& ec, std::size_t bytes_transferred); + + // Constructor records that handler is to be invoked with two arguments. + ASIO_DECL void invocation_begin( + const asio::error_code& ec, int signal_number); + + // Constructor records that handler is to be invoked with two arguments. + ASIO_DECL void invocation_begin( + const asio::error_code& ec, const char* arg); + + // Record that handler invocation has ended. + ASIO_DECL void invocation_end(); + + private: + friend class handler_tracking; + uint64_t id_; + bool invoked_; + completion* next_; + }; + + // Record an operation that is not directly associated with a handler. + ASIO_DECL static void operation(execution_context& context, + const char* object_type, void* object, + uintmax_t native_handle, const char* op_name); + + // Record that a descriptor has been registered with the reactor. + ASIO_DECL static void reactor_registration(execution_context& context, + uintmax_t native_handle, uintmax_t registration); + + // Record that a descriptor has been deregistered from the reactor. + ASIO_DECL static void reactor_deregistration(execution_context& context, + uintmax_t native_handle, uintmax_t registration); + + // Record a reactor-based operation that is associated with a handler. + ASIO_DECL static void reactor_events(execution_context& context, + uintmax_t registration, unsigned events); + + // Record a reactor-based operation that is associated with a handler. + ASIO_DECL static void reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec); + + // Record a reactor-based operation that is associated with a handler. + ASIO_DECL static void reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec, std::size_t bytes_transferred); + + // Write a line of output. + ASIO_DECL static void write_line(const char* format, ...); + +private: + struct tracking_state; + ASIO_DECL static tracking_state* get_state(); +}; + +# define ASIO_INHERIT_TRACKED_HANDLER \ + : public asio::detail::handler_tracking::tracked_handler + +# define ASIO_ALSO_INHERIT_TRACKED_HANDLER \ + , public asio::detail::handler_tracking::tracked_handler + +# define ASIO_HANDLER_TRACKING_INIT \ + asio::detail::handler_tracking::init() + +# define ASIO_HANDLER_CREATION(args) \ + asio::detail::handler_tracking::creation args + +# define ASIO_HANDLER_COMPLETION(args) \ + asio::detail::handler_tracking::completion tracked_completion args + +# define ASIO_HANDLER_INVOCATION_BEGIN(args) \ + tracked_completion.invocation_begin args + +# define ASIO_HANDLER_INVOCATION_END \ + tracked_completion.invocation_end() + +# define ASIO_HANDLER_OPERATION(args) \ + asio::detail::handler_tracking::operation args + +# define ASIO_HANDLER_REACTOR_REGISTRATION(args) \ + asio::detail::handler_tracking::reactor_registration args + +# define ASIO_HANDLER_REACTOR_DEREGISTRATION(args) \ + asio::detail::handler_tracking::reactor_deregistration args + +# define ASIO_HANDLER_REACTOR_READ_EVENT 1 +# define ASIO_HANDLER_REACTOR_WRITE_EVENT 2 +# define ASIO_HANDLER_REACTOR_ERROR_EVENT 4 + +# define ASIO_HANDLER_REACTOR_EVENTS(args) \ + asio::detail::handler_tracking::reactor_events args + +# define ASIO_HANDLER_REACTOR_OPERATION(args) \ + asio::detail::handler_tracking::reactor_operation args + +#else // defined(ASIO_ENABLE_HANDLER_TRACKING) + +# define ASIO_INHERIT_TRACKED_HANDLER +# define ASIO_ALSO_INHERIT_TRACKED_HANDLER +# define ASIO_HANDLER_TRACKING_INIT (void)0 +# define ASIO_HANDLER_CREATION(args) (void)0 +# define ASIO_HANDLER_COMPLETION(args) (void)0 +# define ASIO_HANDLER_INVOCATION_BEGIN(args) (void)0 +# define ASIO_HANDLER_INVOCATION_END (void)0 +# define ASIO_HANDLER_OPERATION(args) (void)0 +# define ASIO_HANDLER_REACTOR_REGISTRATION(args) (void)0 +# define ASIO_HANDLER_REACTOR_DEREGISTRATION(args) (void)0 +# define ASIO_HANDLER_REACTOR_READ_EVENT 0 +# define ASIO_HANDLER_REACTOR_WRITE_EVENT 0 +# define ASIO_HANDLER_REACTOR_ERROR_EVENT 0 +# define ASIO_HANDLER_REACTOR_EVENTS(args) (void)0 +# define ASIO_HANDLER_REACTOR_OPERATION(args) (void)0 + +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/handler_tracking.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_HANDLER_TRACKING_HPP diff --git a/subprojects/asio/include/asio/detail/handler_type_requirements.hpp b/subprojects/asio/include/asio/detail/handler_type_requirements.hpp new file mode 100644 index 000000000..9181bc515 --- /dev/null +++ b/subprojects/asio/include/asio/detail/handler_type_requirements.hpp @@ -0,0 +1,556 @@ +// +// detail/handler_type_requirements.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_HPP +#define ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +// Older versions of gcc have difficulty compiling the sizeof expressions where +// we test the handler type requirements. We'll disable checking of handler type +// requirements for those compilers, but otherwise enable it by default. +#if !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) +# if !defined(__GNUC__) || (__GNUC__ >= 4) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS 1 +# endif // !defined(__GNUC__) || (__GNUC__ >= 4) +#endif // !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +// With C++0x we can use a combination of enhanced SFINAE and static_assert to +// generate better template error messages. As this technique is not yet widely +// portable, we'll only enable it for tested compilers. +#if !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# if defined(__clang__) +# if __has_feature(__cxx_static_assert__) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // __has_feature(cxx_static_assert) +# endif // defined(__clang__) +#endif // !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +#if defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) +# include "asio/async_result.hpp" +#endif // defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +namespace asio { +namespace detail { + +#if defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +# if defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template +auto zero_arg_copyable_handler_test(Handler h, void*) + -> decltype( + sizeof(Handler(static_cast(h))), + ((h)()), + char(0)); + +template +char (&zero_arg_copyable_handler_test(Handler, ...))[2]; + +template +auto one_arg_handler_test(Handler h, Arg1* a1) + -> decltype( + sizeof(Handler(ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1)), + char(0)); + +template +char (&one_arg_handler_test(Handler h, ...))[2]; + +template +auto two_arg_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, *a2)), + char(0)); + +template +char (&two_arg_handler_test(Handler, ...))[2]; + +template +auto two_arg_move_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, ASIO_MOVE_CAST(Arg2)(*a2))), + char(0)); + +template +char (&two_arg_move_handler_test(Handler, ...))[2]; + +# define ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) \ + static_assert(expr, msg); + +# else // defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +# define ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) + +# endif // defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template T& lvref(); +template T& lvref(T); +template const T& clvref(); +template const T& clvref(T); +#if defined(ASIO_HAS_MOVE) +template T rvref(); +template T rvref(T); +#else // defined(ASIO_HAS_MOVE) +template const T& rvref(); +template const T& rvref(T); +#endif // defined(ASIO_HAS_MOVE) +template char argbyv(T); + +template +struct handler_type_requirements +{ +}; + +#define ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void()) asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::zero_arg_copyable_handler_test( \ + asio::detail::clvref< \ + asio_true_handler_type>(), 0)) == 1, \ + "CompletionHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::clvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()(), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_READ_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "ReadHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "WriteHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "AcceptHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, socket_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_move_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "MoveAcceptHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::rvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "ConnectHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, endpoint_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, endpoint_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "RangeConnectHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, iter_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "IteratorConnectHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, range_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, range_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "ResolveHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "WaitHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, int)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "SignalHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "HandshakeHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "BufferedHandshakeHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "ShutdownHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#else // !defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +#define ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_READ_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#endif // !defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_HPP diff --git a/subprojects/asio/include/asio/detail/handler_work.hpp b/subprojects/asio/include/asio/detail/handler_work.hpp new file mode 100644 index 000000000..cce5c4b6d --- /dev/null +++ b/subprojects/asio/include/asio/detail/handler_work.hpp @@ -0,0 +1,95 @@ +// +// detail/handler_work.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_WORK_HPP +#define ASIO_DETAIL_HANDLER_WORK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// A helper class template to allow completion handlers to be dispatched +// through either the new executors framework or the old invocaton hook. The +// primary template uses the new executors framework. +template ::type> +class handler_work +{ +public: + explicit handler_work(Handler& handler) ASIO_NOEXCEPT + : executor_(associated_executor::get(handler)) + { + } + + static void start(Handler& handler) ASIO_NOEXCEPT + { + Executor ex(associated_executor::get(handler)); + ex.on_work_started(); + } + + ~handler_work() + { + executor_.on_work_finished(); + } + + template + void complete(Function& function, Handler& handler) + { + executor_.dispatch(ASIO_MOVE_CAST(Function)(function), + associated_allocator::get(handler)); + } + +private: + // Disallow copying and assignment. + handler_work(const handler_work&); + handler_work& operator=(const handler_work&); + + typename associated_executor::type executor_; +}; + +// This specialisation dispatches a handler through the old invocation hook. +// The specialisation is not strictly required for correctness, as the +// system_executor will dispatch through the hook anyway. However, by doing +// this we avoid an extra copy of the handler. +template +class handler_work +{ +public: + explicit handler_work(Handler&) ASIO_NOEXCEPT {} + static void start(Handler&) ASIO_NOEXCEPT {} + ~handler_work() {} + + template + void complete(Function& function, Handler& handler) + { + asio_handler_invoke_helpers::invoke(function, handler); + } + +private: + // Disallow copying and assignment. + handler_work(const handler_work&); + handler_work& operator=(const handler_work&); +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_WORK_HPP diff --git a/subprojects/asio/include/asio/detail/hash_map.hpp b/subprojects/asio/include/asio/detail/hash_map.hpp new file mode 100644 index 000000000..e70970d6b --- /dev/null +++ b/subprojects/asio/include/asio/detail/hash_map.hpp @@ -0,0 +1,331 @@ +// +// detail/hash_map.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HASH_MAP_HPP +#define ASIO_DETAIL_HASH_MAP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/socket_types.hpp" +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +inline std::size_t calculate_hash_value(int i) +{ + return static_cast(i); +} + +inline std::size_t calculate_hash_value(void* p) +{ + return reinterpret_cast(p) + + (reinterpret_cast(p) >> 3); +} + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +inline std::size_t calculate_hash_value(SOCKET s) +{ + return static_cast(s); +} +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Note: assumes K and V are POD types. +template +class hash_map + : private noncopyable +{ +public: + // The type of a value in the map. + typedef std::pair value_type; + + // The type of a non-const iterator over the hash map. + typedef typename std::list::iterator iterator; + + // The type of a const iterator over the hash map. + typedef typename std::list::const_iterator const_iterator; + + // Constructor. + hash_map() + : size_(0), + buckets_(0), + num_buckets_(0) + { + } + + // Destructor. + ~hash_map() + { + delete[] buckets_; + } + + // Get an iterator for the beginning of the map. + iterator begin() + { + return values_.begin(); + } + + // Get an iterator for the beginning of the map. + const_iterator begin() const + { + return values_.begin(); + } + + // Get an iterator for the end of the map. + iterator end() + { + return values_.end(); + } + + // Get an iterator for the end of the map. + const_iterator end() const + { + return values_.end(); + } + + // Check whether the map is empty. + bool empty() const + { + return values_.empty(); + } + + // Find an entry in the map. + iterator find(const K& k) + { + if (num_buckets_) + { + size_t bucket = calculate_hash_value(k) % num_buckets_; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + return values_.end(); + iterator end_it = buckets_[bucket].last; + ++end_it; + while (it != end_it) + { + if (it->first == k) + return it; + ++it; + } + } + return values_.end(); + } + + // Find an entry in the map. + const_iterator find(const K& k) const + { + if (num_buckets_) + { + size_t bucket = calculate_hash_value(k) % num_buckets_; + const_iterator it = buckets_[bucket].first; + if (it == values_.end()) + return it; + const_iterator end_it = buckets_[bucket].last; + ++end_it; + while (it != end_it) + { + if (it->first == k) + return it; + ++it; + } + } + return values_.end(); + } + + // Insert a new entry into the map. + std::pair insert(const value_type& v) + { + if (size_ + 1 >= num_buckets_) + rehash(hash_size(size_ + 1)); + size_t bucket = calculate_hash_value(v.first) % num_buckets_; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + { + buckets_[bucket].first = buckets_[bucket].last = + values_insert(values_.end(), v); + ++size_; + return std::pair(buckets_[bucket].last, true); + } + iterator end_it = buckets_[bucket].last; + ++end_it; + while (it != end_it) + { + if (it->first == v.first) + return std::pair(it, false); + ++it; + } + buckets_[bucket].last = values_insert(end_it, v); + ++size_; + return std::pair(buckets_[bucket].last, true); + } + + // Erase an entry from the map. + void erase(iterator it) + { + ASIO_ASSERT(it != values_.end()); + ASIO_ASSERT(num_buckets_ != 0); + + size_t bucket = calculate_hash_value(it->first) % num_buckets_; + bool is_first = (it == buckets_[bucket].first); + bool is_last = (it == buckets_[bucket].last); + if (is_first && is_last) + buckets_[bucket].first = buckets_[bucket].last = values_.end(); + else if (is_first) + ++buckets_[bucket].first; + else if (is_last) + --buckets_[bucket].last; + + values_erase(it); + --size_; + } + + // Erase a key from the map. + void erase(const K& k) + { + iterator it = find(k); + if (it != values_.end()) + erase(it); + } + + // Remove all entries from the map. + void clear() + { + // Clear the values. + values_.clear(); + size_ = 0; + + // Initialise all buckets to empty. + iterator end_it = values_.end(); + for (size_t i = 0; i < num_buckets_; ++i) + buckets_[i].first = buckets_[i].last = end_it; + } + +private: + // Calculate the hash size for the specified number of elements. + static std::size_t hash_size(std::size_t num_elems) + { + static std::size_t sizes[] = + { +#if defined(ASIO_HASH_MAP_BUCKETS) + ASIO_HASH_MAP_BUCKETS +#else // ASIO_HASH_MAP_BUCKETS + 3, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, + 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, + 12582917, 25165843 +#endif // ASIO_HASH_MAP_BUCKETS + }; + const std::size_t nth_size = sizeof(sizes) / sizeof(std::size_t) - 1; + for (std::size_t i = 0; i < nth_size; ++i) + if (num_elems < sizes[i]) + return sizes[i]; + return sizes[nth_size]; + } + + // Re-initialise the hash from the values already contained in the list. + void rehash(std::size_t num_buckets) + { + if (num_buckets == num_buckets_) + return; + ASIO_ASSERT(num_buckets != 0); + + iterator end_iter = values_.end(); + + // Update number of buckets and initialise all buckets to empty. + bucket_type* tmp = new bucket_type[num_buckets]; + delete[] buckets_; + buckets_ = tmp; + num_buckets_ = num_buckets; + for (std::size_t i = 0; i < num_buckets_; ++i) + buckets_[i].first = buckets_[i].last = end_iter; + + // Put all values back into the hash. + iterator iter = values_.begin(); + while (iter != end_iter) + { + std::size_t bucket = calculate_hash_value(iter->first) % num_buckets_; + if (buckets_[bucket].last == end_iter) + { + buckets_[bucket].first = buckets_[bucket].last = iter++; + } + else if (++buckets_[bucket].last == iter) + { + ++iter; + } + else + { + values_.splice(buckets_[bucket].last, values_, iter++); + --buckets_[bucket].last; + } + } + } + + // Insert an element into the values list by splicing from the spares list, + // if a spare is available, and otherwise by inserting a new element. + iterator values_insert(iterator it, const value_type& v) + { + if (spares_.empty()) + { + return values_.insert(it, v); + } + else + { + spares_.front() = v; + values_.splice(it, spares_, spares_.begin()); + return --it; + } + } + + // Erase an element from the values list by splicing it to the spares list. + void values_erase(iterator it) + { + *it = value_type(); + spares_.splice(spares_.begin(), values_, it); + } + + // The number of elements in the hash. + std::size_t size_; + + // The list of all values in the hash map. + std::list values_; + + // The list of spare nodes waiting to be recycled. Assumes that POD types only + // are stored in the hash map. + std::list spares_; + + // The type for a bucket in the hash table. + struct bucket_type + { + iterator first; + iterator last; + }; + + // The buckets in the hash. + bucket_type* buckets_; + + // The number of buckets in the hash. + std::size_t num_buckets_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HASH_MAP_HPP diff --git a/subprojects/asio/include/asio/detail/impl/buffer_sequence_adapter.ipp b/subprojects/asio/include/asio/detail/impl/buffer_sequence_adapter.ipp new file mode 100644 index 000000000..323c8adb5 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/buffer_sequence_adapter.ipp @@ -0,0 +1,118 @@ +// +// detail/impl/buffer_sequence_adapter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_BUFFER_SEQUENCE_ADAPTER_IPP +#define ASIO_DETAIL_IMPL_BUFFER_SEQUENCE_ADAPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include +#include +#include "asio/detail/buffer_sequence_adapter.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_buffer_impl : + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>, + ABI::Windows::Storage::Streams::IBuffer, + Windows::Storage::Streams::IBufferByteAccess> +{ +public: + explicit winrt_buffer_impl(const asio::const_buffer& b) + { + bytes_ = const_cast(static_cast(b.data())); + length_ = b.size(); + capacity_ = b.size(); + } + + explicit winrt_buffer_impl(const asio::mutable_buffer& b) + { + bytes_ = static_cast(b.data()); + length_ = 0; + capacity_ = b.size(); + } + + ~winrt_buffer_impl() + { + } + + STDMETHODIMP Buffer(byte** value) + { + *value = bytes_; + return S_OK; + } + + STDMETHODIMP get_Capacity(UINT32* value) + { + *value = capacity_; + return S_OK; + } + + STDMETHODIMP get_Length(UINT32 *value) + { + *value = length_; + return S_OK; + } + + STDMETHODIMP put_Length(UINT32 value) + { + if (value > capacity_) + return E_INVALIDARG; + length_ = value; + return S_OK; + } + +private: + byte* bytes_; + UINT32 length_; + UINT32 capacity_; +}; + +void buffer_sequence_adapter_base::init_native_buffer( + buffer_sequence_adapter_base::native_buffer_type& buf, + const asio::mutable_buffer& buffer) +{ + std::memset(&buf, 0, sizeof(native_buffer_type)); + Microsoft::WRL::ComPtr insp + = Microsoft::WRL::Make(buffer); + buf = reinterpret_cast(insp.Get()); +} + +void buffer_sequence_adapter_base::init_native_buffer( + buffer_sequence_adapter_base::native_buffer_type& buf, + const asio::const_buffer& buffer) +{ + std::memset(&buf, 0, sizeof(native_buffer_type)); + Microsoft::WRL::ComPtr insp + = Microsoft::WRL::Make(buffer); + Platform::Object^ buf_obj = reinterpret_cast(insp.Get()); + buf = reinterpret_cast(insp.Get()); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_BUFFER_SEQUENCE_ADAPTER_IPP diff --git a/subprojects/asio/include/asio/detail/impl/descriptor_ops.ipp b/subprojects/asio/include/asio/detail/impl/descriptor_ops.ipp new file mode 100644 index 000000000..1af643fb5 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/descriptor_ops.ipp @@ -0,0 +1,474 @@ +// +// detail/impl/descriptor_ops.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP +#define ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/descriptor_ops.hpp" +#include "asio/error.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace descriptor_ops { + +int open(const char* path, int flags, asio::error_code& ec) +{ + errno = 0; + int result = error_wrapper(::open(path, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +} + +int close(int d, state_type& state, asio::error_code& ec) +{ + int result = 0; + if (d != -1) + { + errno = 0; + result = error_wrapper(::close(d), ec); + + if (result != 0 + && (ec == asio::error::would_block + || ec == asio::error::try_again)) + { + // According to UNIX Network Programming Vol. 1, it is possible for + // close() to fail with EWOULDBLOCK under certain circumstances. What + // isn't clear is the state of the descriptor after this error. The one + // current OS where this behaviour is seen, Windows, says that the socket + // remains open. Therefore we'll put the descriptor back into blocking + // mode and have another attempt at closing it. +#if defined(__SYMBIAN32__) + int flags = ::fcntl(d, F_GETFL, 0); + if (flags >= 0) + ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); +#else // defined(__SYMBIAN32__) + ioctl_arg_type arg = 0; + ::ioctl(d, FIONBIO, &arg); +#endif // defined(__SYMBIAN32__) + state &= ~non_blocking; + + errno = 0; + result = error_wrapper(::close(d), ec); + } + } + + if (result == 0) + ec = asio::error_code(); + return result; +} + +bool set_user_non_blocking(int d, state_type& state, + bool value, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return false; + } + + errno = 0; +#if defined(__SYMBIAN32__) + int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec); + if (result >= 0) + { + errno = 0; + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = error_wrapper(::fcntl(d, F_SETFL, flag), ec); + } +#else // defined(__SYMBIAN32__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec); +#endif // defined(__SYMBIAN32__) + + if (result >= 0) + { + ec = asio::error_code(); + if (value) + state |= user_set_non_blocking; + else + { + // Clearing the user-set non-blocking mode always overrides any + // internally-set non-blocking flag. Any subsequent asynchronous + // operations will need to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + return true; + } + + return false; +} + +bool set_internal_non_blocking(int d, state_type& state, + bool value, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return false; + } + + if (!value && (state & user_set_non_blocking)) + { + // It does not make sense to clear the internal non-blocking flag if the + // user still wants non-blocking behaviour. Return an error and let the + // caller figure out whether to update the user-set non-blocking flag. + ec = asio::error::invalid_argument; + return false; + } + + errno = 0; +#if defined(__SYMBIAN32__) + int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec); + if (result >= 0) + { + errno = 0; + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = error_wrapper(::fcntl(d, F_SETFL, flag), ec); + } +#else // defined(__SYMBIAN32__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec); +#endif // defined(__SYMBIAN32__) + + if (result >= 0) + { + ec = asio::error_code(); + if (value) + state |= internal_non_blocking; + else + state &= ~internal_non_blocking; + return true; + } + + return false; +} + +std::size_t sync_read(int d, state_type state, buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (all_empty) + { + ec = asio::error_code(); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + errno = 0; + signed_size_type bytes = error_wrapper(::readv( + d, bufs, static_cast(count)), ec); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Check for EOF. + if (bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_read(d, 0, ec) < 0) + return 0; + } +} + +bool non_blocking_read(int d, buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + errno = 0; + signed_size_type bytes = error_wrapper(::readv( + d, bufs, static_cast(count)), ec); + + // Check for end of stream. + if (bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes > 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +std::size_t sync_write(int d, state_type state, const buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes on a stream is a no-op. + if (all_empty) + { + ec = asio::error_code(); + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + errno = 0; + signed_size_type bytes = error_wrapper(::writev( + d, bufs, static_cast(count)), ec); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_write(d, 0, ec) < 0) + return 0; + } +} + +bool non_blocking_write(int d, const buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + errno = 0; + signed_size_type bytes = error_wrapper(::writev( + d, bufs, static_cast(count)), ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +int ioctl(int d, state_type& state, long cmd, + ioctl_arg_type* arg, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + errno = 0; + int result = error_wrapper(::ioctl(d, cmd, arg), ec); + + if (result >= 0) + { + ec = asio::error_code(); + + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the descriptor is + // already in the correct state. This ensures that the underlying + // descriptor is put into the state that has been requested by the user. If + // the ioctl syscall was successful then we need to update the flags to + // match. + if (cmd == static_cast(FIONBIO)) + { + if (*arg) + { + state |= user_set_non_blocking; + } + else + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + } + } + + return result; +} + +int fcntl(int d, int cmd, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + errno = 0; + int result = error_wrapper(::fcntl(d, cmd), ec); + if (result != -1) + ec = asio::error_code(); + return result; +} + +int fcntl(int d, int cmd, long arg, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + errno = 0; + int result = error_wrapper(::fcntl(d, cmd, arg), ec); + if (result != -1) + ec = asio::error_code(); + return result; +} + +int poll_read(int d, state_type state, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLIN; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : -1; + errno = 0; + int result = error_wrapper(::poll(&fds, 1, timeout), ec); + if (result == 0) + ec = (state & user_set_non_blocking) + ? asio::error::would_block : asio::error_code(); + else if (result > 0) + ec = asio::error_code(); + return result; +} + +int poll_write(int d, state_type state, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLOUT; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : -1; + errno = 0; + int result = error_wrapper(::poll(&fds, 1, timeout), ec); + if (result == 0) + ec = (state & user_set_non_blocking) + ? asio::error::would_block : asio::error_code(); + else if (result > 0) + ec = asio::error_code(); + return result; +} + +int poll_error(int d, state_type state, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLPRI | POLLERR | POLLHUP; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : -1; + errno = 0; + int result = error_wrapper(::poll(&fds, 1, timeout), ec); + if (result == 0) + ec = (state & user_set_non_blocking) + ? asio::error::would_block : asio::error_code(); + else if (result > 0) + ec = asio::error_code(); + return result; +} + +} // namespace descriptor_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP diff --git a/subprojects/asio/include/asio/detail/impl/dev_poll_reactor.hpp b/subprojects/asio/include/asio/detail/impl/dev_poll_reactor.hpp new file mode 100644 index 000000000..4cd8aafec --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/dev_poll_reactor.hpp @@ -0,0 +1,91 @@ +// +// detail/impl/dev_poll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP +#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void dev_poll_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void dev_poll_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void dev_poll_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + interrupter_.interrupt(); +} + +template +std::size_t dev_poll_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void dev_poll_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/impl/dev_poll_reactor.ipp b/subprojects/asio/include/asio/detail/impl/dev_poll_reactor.ipp new file mode 100644 index 000000000..1ca376c1c --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/dev_poll_reactor.ipp @@ -0,0 +1,446 @@ +// +// detail/impl/dev_poll_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP +#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/dev_poll_reactor.hpp" +#include "asio/detail/assert.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +dev_poll_reactor::dev_poll_reactor(asio::execution_context& ctx) + : asio::detail::execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(), + dev_poll_fd_(do_dev_poll_create()), + interrupter_(), + shutdown_(false) +{ + // Add the interrupter's descriptor to /dev/poll. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = interrupter_.read_descriptor(); + ev.events = POLLIN | POLLERR; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); +} + +dev_poll_reactor::~dev_poll_reactor() +{ + shutdown(); + ::close(dev_poll_fd_); +} + +void dev_poll_reactor::shutdown() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void dev_poll_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + { + detail::mutex::scoped_lock lock(mutex_); + + if (dev_poll_fd_ != -1) + ::close(dev_poll_fd_); + dev_poll_fd_ = -1; + dev_poll_fd_ = do_dev_poll_create(); + + interrupter_.recreate(); + + // Add the interrupter's descriptor to /dev/poll. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = interrupter_.read_descriptor(); + ev.events = POLLIN | POLLERR; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + + // Re-register all descriptors with /dev/poll. The changes will be written + // to the /dev/poll descriptor the next time the reactor is run. + for (int i = 0; i < max_ops; ++i) + { + reactor_op_queue::iterator iter = op_queue_[i].begin(); + reactor_op_queue::iterator end = op_queue_[i].end(); + for (; iter != end; ++iter) + { + ::pollfd& pending_ev = add_pending_event_change(iter->first); + pending_ev.events |= POLLERR | POLLHUP; + switch (i) + { + case read_op: pending_ev.events |= POLLIN; break; + case write_op: pending_ev.events |= POLLOUT; break; + case except_op: pending_ev.events |= POLLPRI; break; + default: break; + } + } + } + interrupter_.interrupt(); + } +} + +void dev_poll_reactor::init_task() +{ + scheduler_.init_task(); +} + +int dev_poll_reactor::register_descriptor(socket_type, per_descriptor_data&) +{ + return 0; +} + +int dev_poll_reactor::register_internal_descriptor(int op_type, + socket_type descriptor, per_descriptor_data&, reactor_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + op_queue_[op_type].enqueue_operation(descriptor, op); + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLERR | POLLHUP; + switch (op_type) + { + case read_op: ev.events |= POLLIN; break; + case write_op: ev.events |= POLLOUT; break; + case except_op: ev.events |= POLLPRI; break; + default: break; + } + interrupter_.interrupt(); + + return 0; +} + +void dev_poll_reactor::move_descriptor(socket_type, + dev_poll_reactor::per_descriptor_data&, + dev_poll_reactor::per_descriptor_data&) +{ +} + +void dev_poll_reactor::start_op(int op_type, socket_type descriptor, + dev_poll_reactor::per_descriptor_data&, reactor_op* op, + bool is_continuation, bool allow_speculative) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + if (allow_speculative) + { + if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor)) + { + if (!op_queue_[op_type].has_operation(descriptor)) + { + if (op->perform()) + { + lock.unlock(); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + } + } + + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + scheduler_.work_started(); + if (first) + { + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLERR | POLLHUP; + if (op_type == read_op + || op_queue_[read_op].has_operation(descriptor)) + ev.events |= POLLIN; + if (op_type == write_op + || op_queue_[write_op].has_operation(descriptor)) + ev.events |= POLLOUT; + if (op_type == except_op + || op_queue_[except_op].has_operation(descriptor)) + ev.events |= POLLPRI; + interrupter_.interrupt(); + } +} + +void dev_poll_reactor::cancel_ops(socket_type descriptor, + dev_poll_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void dev_poll_reactor::deregister_descriptor(socket_type descriptor, + dev_poll_reactor::per_descriptor_data&, bool) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from /dev/poll. + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLREMOVE; + interrupter_.interrupt(); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void dev_poll_reactor::deregister_internal_descriptor( + socket_type descriptor, dev_poll_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from /dev/poll. Since this function is only called + // during a fork, we can apply the change immediately. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = descriptor; + ev.events = POLLREMOVE; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + + // Destroy all operations associated with the descriptor. + op_queue ops; + asio::error_code ec; + for (int i = 0; i < max_ops; ++i) + op_queue_[i].cancel_operations(descriptor, ops, ec); +} + +void dev_poll_reactor::cleanup_descriptor_data( + dev_poll_reactor::per_descriptor_data&) +{ +} + +void dev_poll_reactor::run(long usec, op_queue& ops) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (usec == 0 && op_queue_[read_op].empty() && op_queue_[write_op].empty() + && op_queue_[except_op].empty() && timer_queues_.all_empty()) + return; + + // Write the pending event registration changes to the /dev/poll descriptor. + std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size(); + if (events_size > 0) + { + errno = 0; + int result = ::write(dev_poll_fd_, + &pending_event_changes_[0], events_size); + if (result != static_cast(events_size)) + { + asio::error_code ec = asio::error_code( + errno, asio::error::get_system_category()); + for (std::size_t i = 0; i < pending_event_changes_.size(); ++i) + { + int descriptor = pending_event_changes_[i].fd; + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + pending_event_changes_.clear(); + pending_event_change_index_.clear(); + } + + // Calculate timeout. + int timeout; + if (usec == 0) + timeout = 0; + else + { + timeout = (usec < 0) ? -1 : ((usec - 1) / 1000 + 1); + timeout = get_timeout(timeout); + } + lock.unlock(); + + // Block on the /dev/poll descriptor. + ::pollfd events[128] = { { 0, 0, 0 } }; + ::dvpoll dp = { 0, 0, 0 }; + dp.dp_fds = events; + dp.dp_nfds = 128; + dp.dp_timeout = timeout; + int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp); + + lock.lock(); + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].fd; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else + { + bool more_reads = false; + bool more_writes = false; + bool more_except = false; + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + if (events[i].events & (POLLPRI | POLLERR | POLLHUP)) + more_except = + op_queue_[except_op].perform_operations(descriptor, ops); + else + more_except = op_queue_[except_op].has_operation(descriptor); + + if (events[i].events & (POLLIN | POLLERR | POLLHUP)) + more_reads = op_queue_[read_op].perform_operations(descriptor, ops); + else + more_reads = op_queue_[read_op].has_operation(descriptor); + + if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) + more_writes = op_queue_[write_op].perform_operations(descriptor, ops); + else + more_writes = op_queue_[write_op].has_operation(descriptor); + + if ((events[i].events & (POLLERR | POLLHUP)) != 0 + && !more_except && !more_reads && !more_writes) + { + // If we have an event and no operations associated with the + // descriptor then we need to delete the descriptor from /dev/poll. + // The poll operation can produce POLLHUP or POLLERR events when there + // is no operation pending, so if we do not remove the descriptor we + // can end up in a tight polling loop. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = descriptor; + ev.events = POLLREMOVE; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + } + else + { + ::pollfd ev = { 0, 0, 0 }; + ev.fd = descriptor; + ev.events = POLLERR | POLLHUP; + if (more_reads) + ev.events |= POLLIN; + if (more_writes) + ev.events |= POLLOUT; + if (more_except) + ev.events |= POLLPRI; + ev.revents = 0; + int result = ::write(dev_poll_fd_, &ev, sizeof(ev)); + if (result != sizeof(ev)) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + } + } + timer_queues_.get_ready_timers(ops); +} + +void dev_poll_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +int dev_poll_reactor::do_dev_poll_create() +{ + int fd = ::open("/dev/poll", O_RDWR); + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "/dev/poll"); + } + return fd; +} + +void dev_poll_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void dev_poll_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +int dev_poll_reactor::get_timeout(int msec) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const int max_msec = 5 * 60 * 1000; + return timer_queues_.wait_duration_msec( + (msec < 0 || max_msec < msec) ? max_msec : msec); +} + +void dev_poll_reactor::cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec) +{ + bool need_interrupt = false; + op_queue ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + scheduler_.post_deferred_completions(ops); + if (need_interrupt) + interrupter_.interrupt(); +} + +::pollfd& dev_poll_reactor::add_pending_event_change(int descriptor) +{ + hash_map::iterator iter + = pending_event_change_index_.find(descriptor); + if (iter == pending_event_change_index_.end()) + { + std::size_t index = pending_event_changes_.size(); + pending_event_changes_.reserve(pending_event_changes_.size() + 1); + pending_event_change_index_.insert(std::make_pair(descriptor, index)); + pending_event_changes_.push_back(::pollfd()); + pending_event_changes_[index].fd = descriptor; + pending_event_changes_[index].revents = 0; + return pending_event_changes_[index]; + } + else + { + return pending_event_changes_[iter->second]; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP diff --git a/subprojects/asio/include/asio/detail/impl/epoll_reactor.hpp b/subprojects/asio/include/asio/detail/impl/epoll_reactor.hpp new file mode 100644 index 000000000..f990059d8 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/epoll_reactor.hpp @@ -0,0 +1,89 @@ +// +// detail/impl/epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void epoll_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void epoll_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void epoll_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + update_timeout(); +} + +template +std::size_t epoll_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void epoll_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/impl/epoll_reactor.ipp b/subprojects/asio/include/asio/detail/impl/epoll_reactor.ipp new file mode 100644 index 000000000..65584a434 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/epoll_reactor.ipp @@ -0,0 +1,787 @@ +// +// detail/impl/epoll_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP +#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include +#include +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_HAS_TIMERFD) +# include +#endif // defined(ASIO_HAS_TIMERFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +epoll_reactor::epoll_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_REGISTRATION, scheduler_.concurrency_hint())), + interrupter_(), + epoll_fd_(do_epoll_create()), + timer_fd_(do_timerfd_create()), + shutdown_(false), + registered_descriptors_mutex_(mutex_.enabled()) +{ + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } +} + +epoll_reactor::~epoll_reactor() +{ + if (epoll_fd_ != -1) + close(epoll_fd_); + if (timer_fd_ != -1) + close(timer_fd_); +} + +void epoll_reactor::shutdown() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + while (descriptor_state* state = registered_descriptors_.first()) + { + for (int i = 0; i < max_ops; ++i) + ops.push(state->op_queue_[i]); + state->shutdown_ = true; + registered_descriptors_.free(state); + } + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void epoll_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + { + if (epoll_fd_ != -1) + ::close(epoll_fd_); + epoll_fd_ = -1; + epoll_fd_ = do_epoll_create(); + + if (timer_fd_ != -1) + ::close(timer_fd_); + timer_fd_ = -1; + timer_fd_ = do_timerfd_create(); + + interrupter_.recreate(); + + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } + + update_timeout(); + + // Re-register all descriptors with epoll. + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + for (descriptor_state* state = registered_descriptors_.first(); + state != 0; state = state->next_) + { + ev.events = state->registered_events_; + ev.data.ptr = state; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, state->descriptor_, &ev); + if (result != 0) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "epoll re-registration"); + } + } + } +} + +void epoll_reactor::init_task() +{ + scheduler_.init_task(); +} + +int epoll_reactor::register_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + descriptor_data->reactor_ = this; + descriptor_data->descriptor_ = descriptor; + descriptor_data->shutdown_ = false; + for (int i = 0; i < max_ops; ++i) + descriptor_data->try_speculative_[i] = true; + } + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET; + descriptor_data->registered_events_ = ev.events; + ev.data.ptr = descriptor_data; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + { + if (errno == EPERM) + { + // This file descriptor type is not supported by epoll. However, if it is + // a regular file then operations on it will not block. We will allow + // this descriptor to be used and fail later if an operation on it would + // otherwise require a trip through the reactor. + descriptor_data->registered_events_ = 0; + return 0; + } + return errno; + } + + return 0; +} + +int epoll_reactor::register_internal_descriptor( + int op_type, socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + descriptor_data->reactor_ = this; + descriptor_data->descriptor_ = descriptor; + descriptor_data->shutdown_ = false; + descriptor_data->op_queue_[op_type].push(op); + for (int i = 0; i < max_ops; ++i) + descriptor_data->try_speculative_[i] = true; + } + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET; + descriptor_data->registered_events_ = ev.events; + ev.data.ptr = descriptor_data; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + return errno; + + return 0; +} + +void epoll_reactor::move_descriptor(socket_type, + epoll_reactor::per_descriptor_data& target_descriptor_data, + epoll_reactor::per_descriptor_data& source_descriptor_data) +{ + target_descriptor_data = source_descriptor_data; + source_descriptor_data = 0; +} + +void epoll_reactor::start_op(int op_type, socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative) +{ + if (!descriptor_data) + { + op->ec_ = asio::error::bad_descriptor; + post_immediate_completion(op, is_continuation); + return; + } + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (descriptor_data->shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + if (descriptor_data->op_queue_[op_type].empty()) + { + if (allow_speculative + && (op_type != read_op + || descriptor_data->op_queue_[except_op].empty())) + { + if (descriptor_data->try_speculative_[op_type]) + { + if (reactor_op::status status = op->perform()) + { + if (status == reactor_op::done_and_exhausted) + if (descriptor_data->registered_events_ != 0) + descriptor_data->try_speculative_[op_type] = false; + descriptor_lock.unlock(); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + + if (descriptor_data->registered_events_ == 0) + { + op->ec_ = asio::error::operation_not_supported; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + if (op_type == write_op) + { + if ((descriptor_data->registered_events_ & EPOLLOUT) == 0) + { + epoll_event ev = { 0, { 0 } }; + ev.events = descriptor_data->registered_events_ | EPOLLOUT; + ev.data.ptr = descriptor_data; + if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev) == 0) + { + descriptor_data->registered_events_ |= ev.events; + } + else + { + op->ec_ = asio::error_code(errno, + asio::error::get_system_category()); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + } + } + else if (descriptor_data->registered_events_ == 0) + { + op->ec_ = asio::error::operation_not_supported; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + else + { + if (op_type == write_op) + { + descriptor_data->registered_events_ |= EPOLLOUT; + } + + epoll_event ev = { 0, { 0 } }; + ev.events = descriptor_data->registered_events_; + ev.data.ptr = descriptor_data; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + } + + descriptor_data->op_queue_[op_type].push(op); + scheduler_.work_started(); +} + +void epoll_reactor::cancel_ops(socket_type, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + scheduler_.post_deferred_completions(ops); +} + +void epoll_reactor::deregister_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, bool closing) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + if (closing) + { + // The descriptor will be automatically removed from the epoll set when + // it is closed. + } + else if (descriptor_data->registered_events_ != 0) + { + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + } + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + scheduler_.post_deferred_completions(ops); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void epoll_reactor::deregister_internal_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + ops.push(descriptor_data->op_queue_[i]); + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void epoll_reactor::cleanup_descriptor_data( + per_descriptor_data& descriptor_data) +{ + if (descriptor_data) + { + free_descriptor_state(descriptor_data); + descriptor_data = 0; + } +} + +void epoll_reactor::run(long usec, op_queue& ops) +{ + // This code relies on the fact that the scheduler queues the reactor task + // behind all descriptor operations generated by this function. This means, + // that by the time we reach this point, any previously returned descriptor + // operations have already been dequeued. Therefore it is now safe for us to + // reuse and return them for the scheduler to queue again. + + // Calculate timeout. Check the timer queues only if timerfd is not in use. + int timeout; + if (usec == 0) + timeout = 0; + else + { + timeout = (usec < 0) ? -1 : ((usec - 1) / 1000 + 1); + if (timer_fd_ == -1) + { + mutex::scoped_lock lock(mutex_); + timeout = get_timeout(timeout); + } + } + + // Block on the epoll descriptor. + epoll_event events[128]; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); + +#if defined(ASIO_ENABLE_HANDLER_TRACKING) + // Trace the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = events[i].data.ptr; + if (ptr == &interrupter_) + { + // Ignore. + } +# if defined(ASIO_HAS_TIMERFD) + else if (ptr == &timer_fd_) + { + // Ignore. + } +# endif // defined(ASIO_HAS_TIMERFD) + else + { + unsigned event_mask = 0; + if ((events[i].events & EPOLLIN) != 0) + event_mask |= ASIO_HANDLER_REACTOR_READ_EVENT; + if ((events[i].events & EPOLLOUT)) + event_mask |= ASIO_HANDLER_REACTOR_WRITE_EVENT; + if ((events[i].events & (EPOLLERR | EPOLLHUP)) != 0) + event_mask |= ASIO_HANDLER_REACTOR_ERROR_EVENT; + ASIO_HANDLER_REACTOR_EVENTS((context(), + reinterpret_cast(ptr), event_mask)); + } + } +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +#if defined(ASIO_HAS_TIMERFD) + bool check_timers = (timer_fd_ == -1); +#else // defined(ASIO_HAS_TIMERFD) + bool check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = events[i].data.ptr; + if (ptr == &interrupter_) + { + // No need to reset the interrupter since we're leaving the descriptor + // in a ready-to-read state and relying on edge-triggered notifications + // to make it so that we only get woken up when the descriptor's epoll + // registration is updated. + +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ == -1) + check_timers = true; +#else // defined(ASIO_HAS_TIMERFD) + check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + } +#if defined(ASIO_HAS_TIMERFD) + else if (ptr == &timer_fd_) + { + check_timers = true; + } +#endif // defined(ASIO_HAS_TIMERFD) + else + { + // The descriptor operation doesn't count as work in and of itself, so we + // don't call work_started() here. This still allows the scheduler to + // stop if the only remaining operations are descriptor operations. + descriptor_state* descriptor_data = static_cast(ptr); + if (!ops.is_enqueued(descriptor_data)) + { + descriptor_data->set_ready_events(events[i].events); + ops.push(descriptor_data); + } + else + { + descriptor_data->add_ready_events(events[i].events); + } + } + } + + if (check_timers) + { + mutex::scoped_lock common_lock(mutex_); + timer_queues_.get_ready_timers(ops); + +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + } +#endif // defined(ASIO_HAS_TIMERFD) + } +} + +void epoll_reactor::interrupt() +{ + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev); +} + +int epoll_reactor::do_epoll_create() +{ +#if defined(EPOLL_CLOEXEC) + int fd = epoll_create1(EPOLL_CLOEXEC); +#else // defined(EPOLL_CLOEXEC) + int fd = -1; + errno = EINVAL; +#endif // defined(EPOLL_CLOEXEC) + + if (fd == -1 && (errno == EINVAL || errno == ENOSYS)) + { + fd = epoll_create(epoll_size); + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + } + + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "epoll"); + } + + return fd; +} + +int epoll_reactor::do_timerfd_create() +{ +#if defined(ASIO_HAS_TIMERFD) +# if defined(TFD_CLOEXEC) + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); +# else // defined(TFD_CLOEXEC) + int fd = -1; + errno = EINVAL; +# endif // defined(TFD_CLOEXEC) + + if (fd == -1 && errno == EINVAL) + { + fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + } + + return fd; +#else // defined(ASIO_HAS_TIMERFD) + return -1; +#endif // defined(ASIO_HAS_TIMERFD) +} + +epoll_reactor::descriptor_state* epoll_reactor::allocate_descriptor_state() +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + return registered_descriptors_.alloc(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_IO, scheduler_.concurrency_hint())); +} + +void epoll_reactor::free_descriptor_state(epoll_reactor::descriptor_state* s) +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + registered_descriptors_.free(s); +} + +void epoll_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void epoll_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +void epoll_reactor::update_timeout() +{ +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + return; + } +#endif // defined(ASIO_HAS_TIMERFD) + interrupt(); +} + +int epoll_reactor::get_timeout(int msec) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const int max_msec = 5 * 60 * 1000; + return timer_queues_.wait_duration_msec( + (msec < 0 || max_msec < msec) ? max_msec : msec); +} + +#if defined(ASIO_HAS_TIMERFD) +int epoll_reactor::get_timeout(itimerspec& ts) +{ + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + ts.it_value.tv_sec = usec / 1000000; + ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1; + + return usec ? 0 : TFD_TIMER_ABSTIME; +} +#endif // defined(ASIO_HAS_TIMERFD) + +struct epoll_reactor::perform_io_cleanup_on_block_exit +{ + explicit perform_io_cleanup_on_block_exit(epoll_reactor* r) + : reactor_(r), first_op_(0) + { + } + + ~perform_io_cleanup_on_block_exit() + { + if (first_op_) + { + // Post the remaining completed operations for invocation. + if (!ops_.empty()) + reactor_->scheduler_.post_deferred_completions(ops_); + + // A user-initiated operation has completed, but there's no need to + // explicitly call work_finished() here. Instead, we'll take advantage of + // the fact that the scheduler will call work_finished() once we return. + } + else + { + // No user-initiated operations have completed, so we need to compensate + // for the work_finished() call that the scheduler will make once this + // operation returns. + reactor_->scheduler_.compensating_work_started(); + } + } + + epoll_reactor* reactor_; + op_queue ops_; + operation* first_op_; +}; + +epoll_reactor::descriptor_state::descriptor_state(bool locking) + : operation(&epoll_reactor::descriptor_state::do_complete), + mutex_(locking) +{ +} + +operation* epoll_reactor::descriptor_state::perform_io(uint32_t events) +{ + mutex_.lock(); + perform_io_cleanup_on_block_exit io_cleanup(reactor_); + mutex::scoped_lock descriptor_lock(mutex_, mutex::scoped_lock::adopt_lock); + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events & (flag[j] | EPOLLERR | EPOLLHUP)) + { + try_speculative_[j] = true; + while (reactor_op* op = op_queue_[j].front()) + { + if (reactor_op::status status = op->perform()) + { + op_queue_[j].pop(); + io_cleanup.ops_.push(op); + if (status == reactor_op::done_and_exhausted) + { + try_speculative_[j] = false; + break; + } + } + else + break; + } + } + } + + // The first operation will be returned for completion now. The others will + // be posted for later by the io_cleanup object's destructor. + io_cleanup.first_op_ = io_cleanup.ops_.front(); + io_cleanup.ops_.pop(); + return io_cleanup.first_op_; +} + +void epoll_reactor::descriptor_state::do_complete( + void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred) +{ + if (owner) + { + descriptor_state* descriptor_data = static_cast(base); + uint32_t events = static_cast(bytes_transferred); + if (operation* op = descriptor_data->perform_io(events)) + { + op->complete(owner, ec, 0); + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP diff --git a/subprojects/asio/include/asio/detail/impl/eventfd_select_interrupter.ipp b/subprojects/asio/include/asio/detail/impl/eventfd_select_interrupter.ipp new file mode 100644 index 000000000..c56e89ad2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/eventfd_select_interrupter.ipp @@ -0,0 +1,165 @@ +// +// detail/impl/eventfd_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EVENTFD) + +#include +#include +#include +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +# include +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +# include +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +#include "asio/detail/cstdint.hpp" +#include "asio/detail/eventfd_select_interrupter.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +eventfd_select_interrupter::eventfd_select_interrupter() +{ + open_descriptors(); +} + +void eventfd_select_interrupter::open_descriptors() +{ +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); + if (read_descriptor_ != -1) + { + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + } +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +# if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + write_descriptor_ = read_descriptor_ = + ::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); +# else // defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + errno = EINVAL; + write_descriptor_ = read_descriptor_ = -1; +# endif // defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + if (read_descriptor_ == -1 && errno == EINVAL) + { + write_descriptor_ = read_descriptor_ = ::eventfd(0, 0); + if (read_descriptor_ != -1) + { + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + } + } +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + + if (read_descriptor_ == -1) + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(write_descriptor_, F_SETFD, FD_CLOEXEC); + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "eventfd_select_interrupter"); + } + } +} + +eventfd_select_interrupter::~eventfd_select_interrupter() +{ + close_descriptors(); +} + +void eventfd_select_interrupter::close_descriptors() +{ + if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) + ::close(write_descriptor_); + if (read_descriptor_ != -1) + ::close(read_descriptor_); +} + +void eventfd_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = -1; + read_descriptor_ = -1; + + open_descriptors(); +} + +void eventfd_select_interrupter::interrupt() +{ + uint64_t counter(1UL); + int result = ::write(write_descriptor_, &counter, sizeof(uint64_t)); + (void)result; +} + +bool eventfd_select_interrupter::reset() +{ + if (write_descriptor_ == read_descriptor_) + { + for (;;) + { + // Only perform one read. The kernel maintains an atomic counter. + uint64_t counter(0); + errno = 0; + int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + return was_interrupted; + } + } + else + { + for (;;) + { + // Clear all data from the pipe. + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EVENTFD) + +#endif // ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP diff --git a/subprojects/asio/include/asio/detail/impl/handler_tracking.ipp b/subprojects/asio/include/asio/detail/impl/handler_tracking.ipp new file mode 100644 index 000000000..5a4ff6f0f --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/handler_tracking.ipp @@ -0,0 +1,358 @@ +// +// detail/impl/handler_tracking.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_HANDLER_TRACKING_IPP +#define ASIO_DETAIL_IMPL_HANDLER_TRACKING_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_CUSTOM_HANDLER_TRACKING) + +// The handler tracking implementation is provided by the user-specified header. + +#elif defined(ASIO_ENABLE_HANDLER_TRACKING) + +#include +#include +#include "asio/detail/handler_tracking.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) +# include "asio/time_traits.hpp" +#elif defined(ASIO_HAS_CHRONO) +# include "asio/detail/chrono.hpp" +# include "asio/detail/chrono_time_traits.hpp" +# include "asio/wait_traits.hpp" +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/socket_types.hpp" +#elif !defined(ASIO_WINDOWS) +# include +#endif // !defined(ASIO_WINDOWS) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct handler_tracking_timestamp +{ + uint64_t seconds; + uint64_t microseconds; + + handler_tracking_timestamp() + { +#if defined(ASIO_HAS_BOOST_DATE_TIME) + boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); + boost::posix_time::time_duration now = + boost::posix_time::microsec_clock::universal_time() - epoch; +#elif defined(ASIO_HAS_CHRONO) + typedef chrono_time_traits > traits_helper; + traits_helper::posix_time_duration now( + chrono::system_clock::now().time_since_epoch()); +#endif + seconds = static_cast(now.total_seconds()); + microseconds = static_cast(now.total_microseconds() % 1000000); + } +}; + +struct handler_tracking::tracking_state +{ + static_mutex mutex_; + uint64_t next_id_; + tss_ptr* current_completion_; +}; + +handler_tracking::tracking_state* handler_tracking::get_state() +{ + static tracking_state state = { ASIO_STATIC_MUTEX_INIT, 1, 0 }; + return &state; +} + +void handler_tracking::init() +{ + static tracking_state* state = get_state(); + + state->mutex_.init(); + + static_mutex::scoped_lock lock(state->mutex_); + if (state->current_completion_ == 0) + state->current_completion_ = new tss_ptr; +} + +void handler_tracking::creation(execution_context&, + handler_tracking::tracked_handler& h, + const char* object_type, void* object, + uintmax_t /*native_handle*/, const char* op_name) +{ + static tracking_state* state = get_state(); + + static_mutex::scoped_lock lock(state->mutex_); + h.id_ = state->next_id_++; + lock.unlock(); + + handler_tracking_timestamp timestamp; + + uint64_t current_id = 0; + if (completion* current_completion = *state->current_completion_) + current_id = current_completion->id_; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|%I64u*%I64u|%.20s@%p.%.50s\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|%llu*%llu|%.20s@%p.%.50s\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + current_id, h.id_, object_type, object, op_name); +} + +handler_tracking::completion::completion( + const handler_tracking::tracked_handler& h) + : id_(h.id_), + invoked_(false), + next_(*get_state()->current_completion_) +{ + *get_state()->current_completion_ = this; +} + +handler_tracking::completion::~completion() +{ + if (id_) + { + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|%c%I64u|\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|%c%llu|\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + invoked_ ? '!' : '~', id_); + } + + *get_state()->current_completion_ = next_; +} + +void handler_tracking::completion::invocation_begin() +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, id_); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value()); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec, std::size_t bytes_transferred) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d,bytes_transferred=%I64u\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d,bytes_transferred=%llu\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value(), + static_cast(bytes_transferred)); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec, int signal_number) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d,signal_number=%d\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d,signal_number=%d\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value(), signal_number); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec, const char* arg) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d,%.50s\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d,%.50s\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value(), arg); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_end() +{ + if (id_) + { + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|<%I64u|\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|<%llu|\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, id_); + + id_ = 0; + } +} + +void handler_tracking::operation(execution_context&, + const char* object_type, void* object, + uintmax_t /*native_handle*/, const char* op_name) +{ + static tracking_state* state = get_state(); + + handler_tracking_timestamp timestamp; + + unsigned long long current_id = 0; + if (completion* current_completion = *state->current_completion_) + current_id = current_completion->id_; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|%I64u|%.20s@%p.%.50s\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|%llu|%.20s@%p.%.50s\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + current_id, object_type, object, op_name); +} + +void handler_tracking::reactor_registration(execution_context& /*context*/, + uintmax_t /*native_handle*/, uintmax_t /*registration*/) +{ +} + +void handler_tracking::reactor_deregistration(execution_context& /*context*/, + uintmax_t /*native_handle*/, uintmax_t /*registration*/) +{ +} + +void handler_tracking::reactor_events(execution_context& /*context*/, + uintmax_t /*native_handle*/, unsigned /*events*/) +{ +} + +void handler_tracking::reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|.%I64u|%s,ec=%.20s:%d\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|.%llu|%s,ec=%.20s:%d\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + h.id_, op_name, ec.category().name(), ec.value()); +} + +void handler_tracking::reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec, std::size_t bytes_transferred) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|.%I64u|%s,ec=%.20s:%d,bytes_transferred=%I64u\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|.%llu|%s,ec=%.20s:%d,bytes_transferred=%llu\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + h.id_, op_name, ec.category().name(), ec.value(), + static_cast(bytes_transferred)); +} + +void handler_tracking::write_line(const char* format, ...) +{ + using namespace std; // For sprintf (or equivalent). + + va_list args; + va_start(args, format); + + char line[256] = ""; +#if defined(ASIO_HAS_SECURE_RTL) + int length = vsprintf_s(line, sizeof(line), format, args); +#else // defined(ASIO_HAS_SECURE_RTL) + int length = vsprintf(line, format, args); +#endif // defined(ASIO_HAS_SECURE_RTL) + + va_end(args); + +#if defined(ASIO_WINDOWS_RUNTIME) + wchar_t wline[256] = L""; + mbstowcs_s(0, wline, sizeof(wline) / sizeof(wchar_t), line, length); + ::OutputDebugStringW(wline); +#elif defined(ASIO_WINDOWS) + HANDLE stderr_handle = ::GetStdHandle(STD_ERROR_HANDLE); + DWORD bytes_written = 0; + ::WriteFile(stderr_handle, line, length, &bytes_written, 0); +#else // defined(ASIO_WINDOWS) + ::write(STDERR_FILENO, line, length); +#endif // defined(ASIO_WINDOWS) +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +#endif // ASIO_DETAIL_IMPL_HANDLER_TRACKING_IPP diff --git a/subprojects/asio/include/asio/detail/impl/kqueue_reactor.hpp b/subprojects/asio/include/asio/detail/impl/kqueue_reactor.hpp new file mode 100644 index 000000000..136d1678d --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/kqueue_reactor.hpp @@ -0,0 +1,93 @@ +// +// detail/impl/kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void kqueue_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void kqueue_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void kqueue_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + interrupt(); +} + +template +std::size_t kqueue_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void kqueue_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/impl/kqueue_reactor.ipp b/subprojects/asio/include/asio/detail/impl/kqueue_reactor.ipp new file mode 100644 index 000000000..73986e0e6 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/kqueue_reactor.ipp @@ -0,0 +1,566 @@ +// +// detail/impl/kqueue_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP +#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/scheduler.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +#if defined(__NetBSD__) +# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ + EV_SET(ev, ident, filt, flags, fflags, data, \ + reinterpret_cast(static_cast(udata))) +#else +# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ + EV_SET(ev, ident, filt, flags, fflags, data, udata) +#endif + +namespace asio { +namespace detail { + +kqueue_reactor::kqueue_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_REGISTRATION, scheduler_.concurrency_hint())), + kqueue_fd_(do_kqueue_create()), + interrupter_(), + shutdown_(false), + registered_descriptors_mutex_(mutex_.enabled()) +{ + struct kevent events[1]; + ASIO_KQUEUE_EV_SET(&events[0], interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD, 0, 0, &interrupter_); + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) + { + asio::error_code error(errno, + asio::error::get_system_category()); + asio::detail::throw_error(error); + } +} + +kqueue_reactor::~kqueue_reactor() +{ + close(kqueue_fd_); +} + +void kqueue_reactor::shutdown() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + while (descriptor_state* state = registered_descriptors_.first()) + { + for (int i = 0; i < max_ops; ++i) + ops.push(state->op_queue_[i]); + state->shutdown_ = true; + registered_descriptors_.free(state); + } + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void kqueue_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + { + // The kqueue descriptor is automatically closed in the child. + kqueue_fd_ = -1; + kqueue_fd_ = do_kqueue_create(); + + interrupter_.recreate(); + + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD, 0, 0, &interrupter_); + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "kqueue interrupter registration"); + } + + // Re-register all descriptors with kqueue. + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + for (descriptor_state* state = registered_descriptors_.first(); + state != 0; state = state->next_) + { + if (state->num_kevents_ > 0) + { + ASIO_KQUEUE_EV_SET(&events[0], state->descriptor_, + EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, state); + ASIO_KQUEUE_EV_SET(&events[1], state->descriptor_, + EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, state); + if (::kevent(kqueue_fd_, events, state->num_kevents_, 0, 0, 0) == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "kqueue re-registration"); + } + } + } + } +} + +void kqueue_reactor::init_task() +{ + scheduler_.init_task(); +} + +int kqueue_reactor::register_descriptor(socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + mutex::scoped_lock lock(descriptor_data->mutex_); + + descriptor_data->descriptor_ = descriptor; + descriptor_data->num_kevents_ = 0; + descriptor_data->shutdown_ = false; + + return 0; +} + +int kqueue_reactor::register_internal_descriptor( + int op_type, socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data, reactor_op* op) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + mutex::scoped_lock lock(descriptor_data->mutex_); + + descriptor_data->descriptor_ = descriptor; + descriptor_data->num_kevents_ = 1; + descriptor_data->shutdown_ = false; + descriptor_data->op_queue_[op_type].push(op); + + struct kevent events[1]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) + return errno; + + return 0; +} + +void kqueue_reactor::move_descriptor(socket_type, + kqueue_reactor::per_descriptor_data& target_descriptor_data, + kqueue_reactor::per_descriptor_data& source_descriptor_data) +{ + target_descriptor_data = source_descriptor_data; + source_descriptor_data = 0; +} + +void kqueue_reactor::start_op(int op_type, socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative) +{ + if (!descriptor_data) + { + op->ec_ = asio::error::bad_descriptor; + post_immediate_completion(op, is_continuation); + return; + } + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (descriptor_data->shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + if (descriptor_data->op_queue_[op_type].empty()) + { + static const int num_kevents[max_ops] = { 1, 2, 1 }; + + if (allow_speculative + && (op_type != read_op + || descriptor_data->op_queue_[except_op].empty())) + { + if (op->perform()) + { + descriptor_lock.unlock(); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + if (descriptor_data->num_kevents_ < num_kevents[op_type]) + { + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + if (::kevent(kqueue_fd_, events, num_kevents[op_type], 0, 0, 0) != -1) + { + descriptor_data->num_kevents_ = num_kevents[op_type]; + } + else + { + op->ec_ = asio::error_code(errno, + asio::error::get_system_category()); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + } + else + { + if (descriptor_data->num_kevents_ < num_kevents[op_type]) + descriptor_data->num_kevents_ = num_kevents[op_type]; + + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); + } + } + + descriptor_data->op_queue_[op_type].push(op); + scheduler_.work_started(); +} + +void kqueue_reactor::cancel_ops(socket_type, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + scheduler_.post_deferred_completions(ops); +} + +void kqueue_reactor::deregister_descriptor(socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data, bool closing) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + if (closing) + { + // The descriptor will be automatically removed from the kqueue when it + // is closed. + } + else + { + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, + EVFILT_READ, EV_DELETE, 0, 0, 0); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, + EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); + } + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + scheduler_.post_deferred_completions(ops); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void kqueue_reactor::deregister_internal_descriptor(socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, + EVFILT_READ, EV_DELETE, 0, 0, 0); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, + EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + ops.push(descriptor_data->op_queue_[i]); + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void kqueue_reactor::cleanup_descriptor_data( + per_descriptor_data& descriptor_data) +{ + if (descriptor_data) + { + free_descriptor_state(descriptor_data); + descriptor_data = 0; + } +} + +void kqueue_reactor::run(long usec, op_queue& ops) +{ + mutex::scoped_lock lock(mutex_); + + // Determine how long to block while waiting for events. + timespec timeout_buf = { 0, 0 }; + timespec* timeout = usec ? get_timeout(usec, timeout_buf) : &timeout_buf; + + lock.unlock(); + + // Block on the kqueue descriptor. + struct kevent events[128]; + int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); + +#if defined(ASIO_ENABLE_HANDLER_TRACKING) + // Trace the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = reinterpret_cast(events[i].udata); + if (ptr != &interrupter_) + { + unsigned event_mask = 0; + switch (events[i].filter) + { + case EVFILT_READ: + event_mask |= ASIO_HANDLER_REACTOR_READ_EVENT; + break; + case EVFILT_WRITE: + event_mask |= ASIO_HANDLER_REACTOR_WRITE_EVENT; + break; + } + if ((events[i].flags & (EV_ERROR | EV_OOBAND)) != 0) + event_mask |= ASIO_HANDLER_REACTOR_ERROR_EVENT; + ASIO_HANDLER_REACTOR_EVENTS((context(), + reinterpret_cast(ptr), event_mask)); + } + } +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = reinterpret_cast(events[i].udata); + if (ptr == &interrupter_) + { + interrupter_.reset(); + } + else + { + descriptor_state* descriptor_data = static_cast(ptr); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (events[i].filter == EVFILT_WRITE + && descriptor_data->num_kevents_ == 2 + && descriptor_data->op_queue_[write_op].empty()) + { + // Some descriptor types, like serial ports, don't seem to support + // EV_CLEAR with EVFILT_WRITE. Since we have no pending write + // operations we'll remove the EVFILT_WRITE registration here so that + // we don't end up in a tight spin. + struct kevent delete_events[1]; + ASIO_KQUEUE_EV_SET(&delete_events[0], + descriptor_data->descriptor_, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, delete_events, 1, 0, 0, 0); + descriptor_data->num_kevents_ = 1; + } + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. +#if defined(__NetBSD__) + static const unsigned int filter[max_ops] = +#else + static const int filter[max_ops] = +#endif + { EVFILT_READ, EVFILT_WRITE, EVFILT_READ }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events[i].filter == filter[j]) + { + if (j != except_op || events[i].flags & EV_OOBAND) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + if (events[i].flags & EV_ERROR) + { + op->ec_ = asio::error_code( + static_cast(events[i].data), + asio::error::get_system_category()); + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + if (op->perform()) + { + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + else + break; + } + } + } + } + } + } + + lock.lock(); + timer_queues_.get_ready_timers(ops); +} + +void kqueue_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +int kqueue_reactor::do_kqueue_create() +{ + int fd = ::kqueue(); + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "kqueue"); + } + return fd; +} + +kqueue_reactor::descriptor_state* kqueue_reactor::allocate_descriptor_state() +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + return registered_descriptors_.alloc(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_IO, scheduler_.concurrency_hint())); +} + +void kqueue_reactor::free_descriptor_state(kqueue_reactor::descriptor_state* s) +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + registered_descriptors_.free(s); +} + +void kqueue_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void kqueue_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +timespec* kqueue_reactor::get_timeout(long usec, timespec& ts) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const long max_usec = 5 * 60 * 1000 * 1000; + usec = timer_queues_.wait_duration_usec( + (usec < 0 || max_usec < usec) ? max_usec : usec); + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + return &ts; +} + +} // namespace detail +} // namespace asio + +#undef ASIO_KQUEUE_EV_SET + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP diff --git a/subprojects/asio/include/asio/detail/impl/null_event.ipp b/subprojects/asio/include/asio/detail/impl/null_event.ipp new file mode 100644 index 000000000..22ade40b6 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/null_event.ipp @@ -0,0 +1,74 @@ +// +// detail/impl/null_event.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_NULL_EVENT_IPP +#define ASIO_DETAIL_IMPL_NULL_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/socket_types.hpp" +#else +# include +# if defined(__hpux) +# include +# endif +# if !defined(__hpux) || defined(__SELECT) +# include +# endif +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void null_event::do_wait() +{ +#if defined(ASIO_WINDOWS_RUNTIME) + std::this_thread::sleep_until((std::chrono::steady_clock::time_point::max)()); +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ::Sleep(INFINITE); +#else + ::pause(); +#endif +} + +void null_event::do_wait_for_usec(long usec) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + std::this_thread::sleep_for(std::chrono::microseconds(usec)); +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ::Sleep(usec / 1000); +#elif defined(__hpux) && defined(__SELECT) + timespec ts; + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + ::pselect(0, 0, 0, 0, &ts, 0); +#else + timeval tv; + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + ::select(0, 0, 0, 0, &tv); +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_NULL_EVENT_IPP diff --git a/subprojects/asio/include/asio/detail/impl/pipe_select_interrupter.ipp b/subprojects/asio/include/asio/detail/impl/pipe_select_interrupter.ipp new file mode 100644 index 000000000..13931ab82 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/pipe_select_interrupter.ipp @@ -0,0 +1,124 @@ +// +// detail/impl/pipe_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) +#if !defined(ASIO_WINDOWS) +#if !defined(__CYGWIN__) +#if !defined(__SYMBIAN32__) +#if !defined(ASIO_HAS_EVENTFD) + +#include +#include +#include +#include +#include "asio/detail/pipe_select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +pipe_select_interrupter::pipe_select_interrupter() +{ + open_descriptors(); +} + +void pipe_select_interrupter::open_descriptors() +{ + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + +#if defined(FD_CLOEXEC) + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + ::fcntl(write_descriptor_, F_SETFD, FD_CLOEXEC); +#endif // defined(FD_CLOEXEC) + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "pipe_select_interrupter"); + } +} + +pipe_select_interrupter::~pipe_select_interrupter() +{ + close_descriptors(); +} + +void pipe_select_interrupter::close_descriptors() +{ + if (read_descriptor_ != -1) + ::close(read_descriptor_); + if (write_descriptor_ != -1) + ::close(write_descriptor_); +} + +void pipe_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = -1; + read_descriptor_ = -1; + + open_descriptors(); +} + +void pipe_select_interrupter::interrupt() +{ + char byte = 0; + signed_size_type result = ::write(write_descriptor_, &byte, 1); + (void)result; +} + +bool pipe_select_interrupter::reset() +{ + for (;;) + { + char data[1024]; + signed_size_type bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_EVENTFD) +#endif // !defined(__SYMBIAN32__) +#endif // !defined(__CYGWIN__) +#endif // !defined(ASIO_WINDOWS) +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP diff --git a/subprojects/asio/include/asio/detail/impl/posix_event.ipp b/subprojects/asio/include/asio/detail/impl/posix_event.ipp new file mode 100644 index 000000000..4b46ab082 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/posix_event.ipp @@ -0,0 +1,59 @@ +// +// detail/impl/posix_event.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_EVENT_IPP +#define ASIO_DETAIL_IMPL_POSIX_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_event.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_event::posix_event() + : state_(0) +{ +#if (defined(__MACH__) && defined(__APPLE__)) \ + || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) + int error = ::pthread_cond_init(&cond_, 0); +#else // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) + ::pthread_condattr_t attr; + ::pthread_condattr_init(&attr); + int error = ::pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + if (error == 0) + error = ::pthread_cond_init(&cond_, &attr); +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) + + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_EVENT_IPP diff --git a/subprojects/asio/include/asio/detail/impl/posix_mutex.ipp b/subprojects/asio/include/asio/detail/impl/posix_mutex.ipp new file mode 100644 index 000000000..27272b57f --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/posix_mutex.ipp @@ -0,0 +1,46 @@ +// +// detail/impl/posix_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP +#define ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_mutex.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_mutex::posix_mutex() +{ + int error = ::pthread_mutex_init(&mutex_, 0); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "mutex"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP diff --git a/subprojects/asio/include/asio/detail/impl/posix_thread.ipp b/subprojects/asio/include/asio/detail/impl/posix_thread.ipp new file mode 100644 index 000000000..69bd16f9f --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/posix_thread.ipp @@ -0,0 +1,84 @@ +// +// detail/impl/posix_thread.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_THREAD_IPP +#define ASIO_DETAIL_IMPL_POSIX_THREAD_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_thread.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_thread::~posix_thread() +{ + if (!joined_) + ::pthread_detach(thread_); +} + +void posix_thread::join() +{ + if (!joined_) + { + ::pthread_join(thread_, 0); + joined_ = true; + } +} + +std::size_t posix_thread::hardware_concurrency() +{ +#if defined(_SC_NPROCESSORS_ONLN) + long result = sysconf(_SC_NPROCESSORS_ONLN); + if (result > 0) + return result; +#endif // defined(_SC_NPROCESSORS_ONLN) + return 0; +} + +void posix_thread::start_thread(func_base* arg) +{ + int error = ::pthread_create(&thread_, 0, + asio_detail_posix_thread_function, arg); + if (error != 0) + { + delete arg; + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } +} + +void* asio_detail_posix_thread_function(void* arg) +{ + posix_thread::auto_func_base_ptr func = { + static_cast(arg) }; + func.ptr->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_THREAD_IPP diff --git a/subprojects/asio/include/asio/detail/impl/posix_tss_ptr.ipp b/subprojects/asio/include/asio/detail/impl/posix_tss_ptr.ipp new file mode 100644 index 000000000..54b58bd1e --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/posix_tss_ptr.ipp @@ -0,0 +1,46 @@ +// +// detail/impl/posix_tss_ptr.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP +#define ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_tss_ptr.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void posix_tss_ptr_create(pthread_key_t& key) +{ + int error = ::pthread_key_create(&key, 0); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "tss"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP diff --git a/subprojects/asio/include/asio/detail/impl/reactive_descriptor_service.ipp b/subprojects/asio/include/asio/detail/impl/reactive_descriptor_service.ipp new file mode 100644 index 000000000..f9505ca95 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/reactive_descriptor_service.ipp @@ -0,0 +1,222 @@ +// +// detail/impl/reactive_descriptor_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include "asio/error.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_descriptor_service::reactive_descriptor_service( + asio::io_context& io_context) + : service_base(io_context), + reactor_(asio::use_service(io_context)) +{ + reactor_.init_task(); +} + +void reactive_descriptor_service::shutdown() +{ +} + +void reactive_descriptor_service::construct( + reactive_descriptor_service::implementation_type& impl) +{ + impl.descriptor_ = -1; + impl.state_ = 0; +} + +void reactive_descriptor_service::move_construct( + reactive_descriptor_service::implementation_type& impl, + reactive_descriptor_service::implementation_type& other_impl) +{ + impl.descriptor_ = other_impl.descriptor_; + other_impl.descriptor_ = -1; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + reactor_.move_descriptor(impl.descriptor_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_descriptor_service::move_assign( + reactive_descriptor_service::implementation_type& impl, + reactive_descriptor_service& other_service, + reactive_descriptor_service::implementation_type& other_impl) +{ + destroy(impl); + + impl.descriptor_ = other_impl.descriptor_; + other_impl.descriptor_ = -1; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + other_service.reactor_.move_descriptor(impl.descriptor_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_descriptor_service::destroy( + reactive_descriptor_service::implementation_type& impl) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "close")); + + reactor_.deregister_descriptor(impl.descriptor_, impl.reactor_data_, + (impl.state_ & descriptor_ops::possible_dup) == 0); + + asio::error_code ignored_ec; + descriptor_ops::close(impl.descriptor_, impl.state_, ignored_ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } +} + +asio::error_code reactive_descriptor_service::assign( + reactive_descriptor_service::implementation_type& impl, + const native_handle_type& native_descriptor, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_descriptor, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.descriptor_ = native_descriptor; + impl.state_ = descriptor_ops::possible_dup; + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_descriptor_service::close( + reactive_descriptor_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "close")); + + reactor_.deregister_descriptor(impl.descriptor_, impl.reactor_data_, + (impl.state_ & descriptor_ops::possible_dup) == 0); + + descriptor_ops::close(impl.descriptor_, impl.state_, ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = asio::error_code(); + } + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour.) + construct(impl); + + return ec; +} + +reactive_descriptor_service::native_handle_type +reactive_descriptor_service::release( + reactive_descriptor_service::implementation_type& impl) +{ + native_handle_type descriptor = impl.descriptor_; + + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "release")); + + reactor_.deregister_descriptor(impl.descriptor_, impl.reactor_data_, false); + reactor_.cleanup_descriptor_data(impl.reactor_data_); + construct(impl); + } + + return descriptor; +} + +asio::error_code reactive_descriptor_service::cancel( + reactive_descriptor_service::implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "cancel")); + + reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_); + ec = asio::error_code(); + return ec; +} + +void reactive_descriptor_service::start_op( + reactive_descriptor_service::implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool is_non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & descriptor_ops::non_blocking) || + descriptor_ops::set_internal_non_blocking( + impl.descriptor_, impl.state_, true, op->ec_)) + { + reactor_.start_op(op_type, impl.descriptor_, + impl.reactor_data_, op, is_continuation, is_non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/reactive_serial_port_service.ipp b/subprojects/asio/include/asio/detail/impl/reactive_serial_port_service.ipp new file mode 100644 index 000000000..c907835c7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/reactive_serial_port_service.ipp @@ -0,0 +1,152 @@ +// +// detail/impl/reactive_serial_port_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include +#include "asio/detail/reactive_serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_serial_port_service::reactive_serial_port_service( + asio::io_context& io_context) + : service_base(io_context), + descriptor_service_(io_context) +{ +} + +void reactive_serial_port_service::shutdown() +{ + descriptor_service_.shutdown(); +} + +asio::error_code reactive_serial_port_service::open( + reactive_serial_port_service::implementation_type& impl, + const std::string& device, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + descriptor_ops::state_type state = 0; + int fd = descriptor_ops::open(device.c_str(), + O_RDWR | O_NONBLOCK | O_NOCTTY, ec); + if (fd < 0) + return ec; + + int s = descriptor_ops::fcntl(fd, F_GETFL, ec); + if (s >= 0) + s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec); + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + return ec; + } + + // Set up default serial port options. + termios ios; + errno = 0; + s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec); + if (s >= 0) + { +#if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE) + ::cfmakeraw(&ios); +#else + ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK + | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + ios.c_oflag &= ~OPOST; + ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + ios.c_cflag &= ~(CSIZE | PARENB); + ios.c_cflag |= CS8; +#endif + ios.c_iflag |= IGNPAR; + ios.c_cflag |= CREAD | CLOCAL; + errno = 0; + s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec); + } + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + return ec; + } + + // We're done. Take ownership of the serial port descriptor. + if (descriptor_service_.assign(impl, fd, ec)) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + } + + return ec; +} + +asio::error_code reactive_serial_port_service::do_set_option( + reactive_serial_port_service::implementation_type& impl, + reactive_serial_port_service::store_function_type store, + const void* option, asio::error_code& ec) +{ + termios ios; + errno = 0; + descriptor_ops::error_wrapper(::tcgetattr( + descriptor_service_.native_handle(impl), &ios), ec); + if (ec) + return ec; + + if (store(option, ios, ec)) + return ec; + + errno = 0; + descriptor_ops::error_wrapper(::tcsetattr( + descriptor_service_.native_handle(impl), TCSANOW, &ios), ec); + return ec; +} + +asio::error_code reactive_serial_port_service::do_get_option( + const reactive_serial_port_service::implementation_type& impl, + reactive_serial_port_service::load_function_type load, + void* option, asio::error_code& ec) const +{ + termios ios; + errno = 0; + descriptor_ops::error_wrapper(::tcgetattr( + descriptor_service_.native_handle(impl), &ios), ec); + if (ec) + return ec; + + return load(option, ios, ec); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) +#endif // defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/reactive_socket_service_base.ipp b/subprojects/asio/include/asio/detail/impl/reactive_socket_service_base.ipp new file mode 100644 index 000000000..129b851da --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/reactive_socket_service_base.ipp @@ -0,0 +1,300 @@ +// +// detail/reactive_socket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) \ + && !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/reactive_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_socket_service_base::reactive_socket_service_base( + asio::io_context& io_context) + : io_context_(io_context), + reactor_(use_service(io_context)) +{ + reactor_.init_task(); +} + +void reactive_socket_service_base::base_shutdown() +{ +} + +void reactive_socket_service_base::construct( + reactive_socket_service_base::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; +} + +void reactive_socket_service_base::base_move_construct( + reactive_socket_service_base::base_implementation_type& impl, + reactive_socket_service_base::base_implementation_type& other_impl) +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base::base_move_assign( + reactive_socket_service_base::base_implementation_type& impl, + reactive_socket_service_base& other_service, + reactive_socket_service_base::base_implementation_type& other_impl) +{ + destroy(impl); + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + other_service.reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base::destroy( + reactive_socket_service_base::base_implementation_type& impl) +{ + if (impl.socket_ != invalid_socket) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } +} + +asio::error_code reactive_socket_service_base::close( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + socket_ops::close(impl.socket_, impl.state_, false, ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = asio::error_code(); + } + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour. The + // known exception is when Windows's closesocket() function fails with + // WSAEWOULDBLOCK, but this case is handled inside socket_ops::close(). + construct(impl); + + return ec; +} + +socket_type reactive_socket_service_base::release( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return invalid_socket; + } + + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "release")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, false); + reactor_.cleanup_descriptor_data(impl.reactor_data_); + socket_type sock = impl.socket_; + construct(impl); + ec = asio::error_code(); + return sock; +} + +asio::error_code reactive_socket_service_base::cancel( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "cancel")); + + reactor_.cancel_ops(impl.socket_, impl.reactor_data_); + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_socket_service_base::do_open( + reactive_socket_service_base::base_implementation_type& impl, + int af, int type, int protocol, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(af, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_socket_service_base::do_assign( + reactive_socket_service_base::base_implementation_type& impl, int type, + const reactive_socket_service_base::native_handle_type& native_socket, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_socket, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.state_ |= socket_ops::possible_dup; + ec = asio::error_code(); + return ec; +} + +void reactive_socket_service_base::start_op( + reactive_socket_service_base::base_implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool is_non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + reactor_.start_op(op_type, impl.socket_, + impl.reactor_data_, op, is_continuation, is_non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +void reactive_socket_service_base::start_accept_op( + reactive_socket_service_base::base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open) +{ + if (!peer_is_open) + start_op(impl, reactor::read_op, op, is_continuation, true, false); + else + { + op->ec_ = asio::error::already_open; + reactor_.post_immediate_completion(op, is_continuation); + } +} + +void reactive_socket_service_base::start_connect_op( + reactive_socket_service_base::base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen) +{ + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + reactor_.start_op(reactor::connect_op, impl.socket_, + impl.reactor_data_, op, is_continuation, false); + return; + } + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_IOCP) + // && !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/resolver_service_base.ipp b/subprojects/asio/include/asio/detail/impl/resolver_service_base.ipp new file mode 100644 index 000000000..540bb66be --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/resolver_service_base.ipp @@ -0,0 +1,154 @@ +// +// detail/impl/resolver_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/resolver_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolver_service_base::work_io_context_runner +{ +public: + work_io_context_runner(asio::io_context& io_context) + : io_context_(io_context) {} + void operator()() { io_context_.run(); } +private: + asio::io_context& io_context_; +}; + +resolver_service_base::resolver_service_base( + asio::io_context& io_context) + : io_context_impl_(asio::use_service(io_context)), + work_io_context_(new asio::io_context(-1)), + work_io_context_impl_(asio::use_service< + io_context_impl>(*work_io_context_)), + work_(asio::make_work_guard(*work_io_context_)), + work_thread_(0) +{ +} + +resolver_service_base::~resolver_service_base() +{ + base_shutdown(); +} + +void resolver_service_base::base_shutdown() +{ + work_.reset(); + if (work_io_context_.get()) + { + work_io_context_->stop(); + if (work_thread_.get()) + { + work_thread_->join(); + work_thread_.reset(); + } + work_io_context_.reset(); + } +} + +void resolver_service_base::base_notify_fork( + asio::io_context::fork_event fork_ev) +{ + if (work_thread_.get()) + { + if (fork_ev == asio::io_context::fork_prepare) + { + work_io_context_->stop(); + work_thread_->join(); + } + else + { + work_io_context_->restart(); + work_thread_.reset(new asio::detail::thread( + work_io_context_runner(*work_io_context_))); + } + } +} + +void resolver_service_base::construct( + resolver_service_base::implementation_type& impl) +{ + impl.reset(static_cast(0), socket_ops::noop_deleter()); +} + +void resolver_service_base::destroy( + resolver_service_base::implementation_type& impl) +{ + ASIO_HANDLER_OPERATION((io_context_impl_.context(), + "resolver", &impl, 0, "cancel")); + + impl.reset(); +} + +void resolver_service_base::move_construct(implementation_type& impl, + implementation_type& other_impl) +{ + impl = ASIO_MOVE_CAST(implementation_type)(other_impl); +} + +void resolver_service_base::move_assign(implementation_type& impl, + resolver_service_base&, implementation_type& other_impl) +{ + destroy(impl); + impl = ASIO_MOVE_CAST(implementation_type)(other_impl); +} + +void resolver_service_base::cancel( + resolver_service_base::implementation_type& impl) +{ + ASIO_HANDLER_OPERATION((io_context_impl_.context(), + "resolver", &impl, 0, "cancel")); + + impl.reset(static_cast(0), socket_ops::noop_deleter()); +} + +void resolver_service_base::start_resolve_op(resolve_op* op) +{ + if (ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER, + io_context_impl_.concurrency_hint())) + { + start_work_thread(); + io_context_impl_.work_started(); + work_io_context_impl_.post_immediate_completion(op, false); + } + else + { + op->ec_ = asio::error::operation_not_supported; + io_context_impl_.post_immediate_completion(op, false); + } +} + +void resolver_service_base::start_work_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + if (!work_thread_.get()) + { + work_thread_.reset(new asio::detail::thread( + work_io_context_runner(*work_io_context_))); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/scheduler.ipp b/subprojects/asio/include/asio/detail/impl/scheduler.ipp new file mode 100644 index 000000000..35bc67879 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/scheduler.ipp @@ -0,0 +1,571 @@ +// +// detail/impl/scheduler.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SCHEDULER_IPP +#define ASIO_DETAIL_IMPL_SCHEDULER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/concurrency_hint.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/scheduler.hpp" +#include "asio/detail/scheduler_thread_info.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct scheduler::task_cleanup +{ + ~task_cleanup() + { + if (this_thread_->private_outstanding_work > 0) + { + asio::detail::increment( + scheduler_->outstanding_work_, + this_thread_->private_outstanding_work); + } + this_thread_->private_outstanding_work = 0; + + // Enqueue the completed operations and reinsert the task at the end of + // the operation queue. + lock_->lock(); + scheduler_->task_interrupted_ = true; + scheduler_->op_queue_.push(this_thread_->private_op_queue); + scheduler_->op_queue_.push(&scheduler_->task_operation_); + } + + scheduler* scheduler_; + mutex::scoped_lock* lock_; + thread_info* this_thread_; +}; + +struct scheduler::work_cleanup +{ + ~work_cleanup() + { + if (this_thread_->private_outstanding_work > 1) + { + asio::detail::increment( + scheduler_->outstanding_work_, + this_thread_->private_outstanding_work - 1); + } + else if (this_thread_->private_outstanding_work < 1) + { + scheduler_->work_finished(); + } + this_thread_->private_outstanding_work = 0; + +#if defined(ASIO_HAS_THREADS) + if (!this_thread_->private_op_queue.empty()) + { + lock_->lock(); + scheduler_->op_queue_.push(this_thread_->private_op_queue); + } +#endif // defined(ASIO_HAS_THREADS) + } + + scheduler* scheduler_; + mutex::scoped_lock* lock_; + thread_info* this_thread_; +}; + +scheduler::scheduler( + asio::execution_context& ctx, int concurrency_hint) + : asio::detail::execution_context_service_base(ctx), + one_thread_(concurrency_hint == 1 + || !ASIO_CONCURRENCY_HINT_IS_LOCKING( + SCHEDULER, concurrency_hint) + || !ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_IO, concurrency_hint)), + mutex_(ASIO_CONCURRENCY_HINT_IS_LOCKING( + SCHEDULER, concurrency_hint)), + task_(0), + task_interrupted_(true), + outstanding_work_(0), + stopped_(false), + shutdown_(false), + concurrency_hint_(concurrency_hint) +{ + ASIO_HANDLER_TRACKING_INIT; +} + +void scheduler::shutdown() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + // Destroy handler objects. + while (!op_queue_.empty()) + { + operation* o = op_queue_.front(); + op_queue_.pop(); + if (o != &task_operation_) + o->destroy(); + } + + // Reset to initial state. + task_ = 0; +} + +void scheduler::init_task() +{ + mutex::scoped_lock lock(mutex_); + if (!shutdown_ && !task_) + { + task_ = &use_service(this->context()); + op_queue_.push(&task_operation_); + wake_one_thread_and_unlock(lock); + } +} + +std::size_t scheduler::run(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + + std::size_t n = 0; + for (; do_run_one(lock, this_thread, ec); lock.lock()) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +std::size_t scheduler::run_one(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + + return do_run_one(lock, this_thread, ec); +} + +std::size_t scheduler::wait_one(long usec, asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + + return do_wait_one(lock, this_thread, usec, ec); +} + +std::size_t scheduler::poll(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + +#if defined(ASIO_HAS_THREADS) + // We want to support nested calls to poll() and poll_one(), so any handlers + // that are already on a thread-private queue need to be put on to the main + // queue now. + if (one_thread_) + if (thread_info* outer_info = static_cast(ctx.next_by_key())) + op_queue_.push(outer_info->private_op_queue); +#endif // defined(ASIO_HAS_THREADS) + + std::size_t n = 0; + for (; do_poll_one(lock, this_thread, ec); lock.lock()) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +std::size_t scheduler::poll_one(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + +#if defined(ASIO_HAS_THREADS) + // We want to support nested calls to poll() and poll_one(), so any handlers + // that are already on a thread-private queue need to be put on to the main + // queue now. + if (one_thread_) + if (thread_info* outer_info = static_cast(ctx.next_by_key())) + op_queue_.push(outer_info->private_op_queue); +#endif // defined(ASIO_HAS_THREADS) + + return do_poll_one(lock, this_thread, ec); +} + +void scheduler::stop() +{ + mutex::scoped_lock lock(mutex_); + stop_all_threads(lock); +} + +bool scheduler::stopped() const +{ + mutex::scoped_lock lock(mutex_); + return stopped_; +} + +void scheduler::restart() +{ + mutex::scoped_lock lock(mutex_); + stopped_ = false; +} + +void scheduler::compensating_work_started() +{ + thread_info_base* this_thread = thread_call_stack::contains(this); + ++static_cast(this_thread)->private_outstanding_work; +} + +void scheduler::post_immediate_completion( + scheduler::operation* op, bool is_continuation) +{ +#if defined(ASIO_HAS_THREADS) + if (one_thread_ || is_continuation) + { + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + { + ++static_cast(this_thread)->private_outstanding_work; + static_cast(this_thread)->private_op_queue.push(op); + return; + } + } +#else // defined(ASIO_HAS_THREADS) + (void)is_continuation; +#endif // defined(ASIO_HAS_THREADS) + + work_started(); + mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); +} + +void scheduler::post_deferred_completion(scheduler::operation* op) +{ +#if defined(ASIO_HAS_THREADS) + if (one_thread_) + { + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + { + static_cast(this_thread)->private_op_queue.push(op); + return; + } + } +#endif // defined(ASIO_HAS_THREADS) + + mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); +} + +void scheduler::post_deferred_completions( + op_queue& ops) +{ + if (!ops.empty()) + { +#if defined(ASIO_HAS_THREADS) + if (one_thread_) + { + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + { + static_cast(this_thread)->private_op_queue.push(ops); + return; + } + } +#endif // defined(ASIO_HAS_THREADS) + + mutex::scoped_lock lock(mutex_); + op_queue_.push(ops); + wake_one_thread_and_unlock(lock); + } +} + +void scheduler::do_dispatch( + scheduler::operation* op) +{ + work_started(); + mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); +} + +void scheduler::abandon_operations( + op_queue& ops) +{ + op_queue ops2; + ops2.push(ops); +} + +std::size_t scheduler::do_run_one(mutex::scoped_lock& lock, + scheduler::thread_info& this_thread, + const asio::error_code& ec) +{ + while (!stopped_) + { + if (!op_queue_.empty()) + { + // Prepare to execute first handler from queue. + operation* o = op_queue_.front(); + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + if (o == &task_operation_) + { + task_interrupted_ = more_handlers; + + if (more_handlers && !one_thread_) + wakeup_event_.unlock_and_signal_one(lock); + else + lock.unlock(); + + task_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(more_handlers ? 0 : -1, this_thread.private_op_queue); + } + else + { + std::size_t task_result = o->task_result_; + + if (more_handlers && !one_thread_) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Complete the operation. May throw an exception. Deletes the object. + o->complete(this, ec, task_result); + + return 1; + } + } + else + { + wakeup_event_.clear(lock); + wakeup_event_.wait(lock); + } + } + + return 0; +} + +std::size_t scheduler::do_wait_one(mutex::scoped_lock& lock, + scheduler::thread_info& this_thread, long usec, + const asio::error_code& ec) +{ + if (stopped_) + return 0; + + operation* o = op_queue_.front(); + if (o == 0) + { + wakeup_event_.clear(lock); + wakeup_event_.wait_for_usec(lock, usec); + usec = 0; // Wait at most once. + o = op_queue_.front(); + } + + if (o == &task_operation_) + { + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + task_interrupted_ = more_handlers; + + if (more_handlers && !one_thread_) + wakeup_event_.unlock_and_signal_one(lock); + else + lock.unlock(); + + { + task_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(more_handlers ? 0 : usec, this_thread.private_op_queue); + } + + o = op_queue_.front(); + if (o == &task_operation_) + { + if (!one_thread_) + wakeup_event_.maybe_unlock_and_signal_one(lock); + return 0; + } + } + + if (o == 0) + return 0; + + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + std::size_t task_result = o->task_result_; + + if (more_handlers && !one_thread_) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Complete the operation. May throw an exception. Deletes the object. + o->complete(this, ec, task_result); + + return 1; +} + +std::size_t scheduler::do_poll_one(mutex::scoped_lock& lock, + scheduler::thread_info& this_thread, + const asio::error_code& ec) +{ + if (stopped_) + return 0; + + operation* o = op_queue_.front(); + if (o == &task_operation_) + { + op_queue_.pop(); + lock.unlock(); + + { + task_cleanup c = { this, &lock, &this_thread }; + (void)c; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(0, this_thread.private_op_queue); + } + + o = op_queue_.front(); + if (o == &task_operation_) + { + wakeup_event_.maybe_unlock_and_signal_one(lock); + return 0; + } + } + + if (o == 0) + return 0; + + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + std::size_t task_result = o->task_result_; + + if (more_handlers && !one_thread_) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Complete the operation. May throw an exception. Deletes the object. + o->complete(this, ec, task_result); + + return 1; +} + +void scheduler::stop_all_threads( + mutex::scoped_lock& lock) +{ + stopped_ = true; + wakeup_event_.signal_all(lock); + + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } +} + +void scheduler::wake_one_thread_and_unlock( + mutex::scoped_lock& lock) +{ + if (!wakeup_event_.maybe_unlock_and_signal_one(lock)) + { + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } + lock.unlock(); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SCHEDULER_IPP diff --git a/subprojects/asio/include/asio/detail/impl/select_reactor.hpp b/subprojects/asio/include/asio/detail/impl/select_reactor.hpp new file mode 100644 index 000000000..04a04d49f --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/select_reactor.hpp @@ -0,0 +1,100 @@ +// +// detail/impl/select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE) \ + && !defined(ASIO_WINDOWS_RUNTIME)) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void select_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void select_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void select_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + interrupter_.interrupt(); +} + +template +std::size_t select_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void select_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE) + // && !defined(ASIO_WINDOWS_RUNTIME)) + +#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/impl/select_reactor.ipp b/subprojects/asio/include/asio/detail/impl/select_reactor.ipp new file mode 100644 index 000000000..262bc6980 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/select_reactor.ipp @@ -0,0 +1,333 @@ +// +// detail/impl/select_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP +#define ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE) \ + && !defined(ASIO_WINDOWS_RUNTIME)) + +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) +class select_reactor::thread_function +{ +public: + explicit thread_function(select_reactor* r) + : this_(r) + { + } + + void operator()() + { + this_->run_thread(); + } + +private: + select_reactor* this_; +}; +#endif // defined(ASIO_HAS_IOCP) + +select_reactor::select_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(), + interrupter_(), +#if defined(ASIO_HAS_IOCP) + stop_thread_(false), + thread_(0), +#endif // defined(ASIO_HAS_IOCP) + shutdown_(false) +{ +#if defined(ASIO_HAS_IOCP) + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread(thread_function(this)); +#endif // defined(ASIO_HAS_IOCP) +} + +select_reactor::~select_reactor() +{ + shutdown(); +} + +void select_reactor::shutdown() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; +#if defined(ASIO_HAS_IOCP) + stop_thread_ = true; +#endif // defined(ASIO_HAS_IOCP) + lock.unlock(); + +#if defined(ASIO_HAS_IOCP) + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } +#endif // defined(ASIO_HAS_IOCP) + + op_queue ops; + + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void select_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + interrupter_.recreate(); +} + +void select_reactor::init_task() +{ + scheduler_.init_task(); +} + +int select_reactor::register_descriptor(socket_type, + select_reactor::per_descriptor_data&) +{ + return 0; +} + +int select_reactor::register_internal_descriptor( + int op_type, socket_type descriptor, + select_reactor::per_descriptor_data&, reactor_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + op_queue_[op_type].enqueue_operation(descriptor, op); + interrupter_.interrupt(); + + return 0; +} + +void select_reactor::move_descriptor(socket_type, + select_reactor::per_descriptor_data&, + select_reactor::per_descriptor_data&) +{ +} + +void select_reactor::start_op(int op_type, socket_type descriptor, + select_reactor::per_descriptor_data&, reactor_op* op, + bool is_continuation, bool) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + scheduler_.work_started(); + if (first) + interrupter_.interrupt(); +} + +void select_reactor::cancel_ops(socket_type descriptor, + select_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void select_reactor::deregister_descriptor(socket_type descriptor, + select_reactor::per_descriptor_data&, bool) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void select_reactor::deregister_internal_descriptor( + socket_type descriptor, select_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + for (int i = 0; i < max_ops; ++i) + op_queue_[i].cancel_operations(descriptor, ops); +} + +void select_reactor::cleanup_descriptor_data( + select_reactor::per_descriptor_data&) +{ +} + +void select_reactor::run(long usec, op_queue& ops) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + +#if defined(ASIO_HAS_IOCP) + // Check if the thread is supposed to stop. + if (stop_thread_) + return; +#endif // defined(ASIO_HAS_IOCP) + + // Set up the descriptor sets. + for (int i = 0; i < max_select_ops; ++i) + fd_sets_[i].reset(); + fd_sets_[read_op].set(interrupter_.read_descriptor()); + socket_type max_fd = 0; + bool have_work_to_do = !timer_queues_.all_empty(); + for (int i = 0; i < max_select_ops; ++i) + { + have_work_to_do = have_work_to_do || !op_queue_[i].empty(); + fd_sets_[i].set(op_queue_[i], ops); + if (fd_sets_[i].max_descriptor() > max_fd) + max_fd = fd_sets_[i].max_descriptor(); + } + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty(); + fd_sets_[write_op].set(op_queue_[connect_op], ops); + if (fd_sets_[write_op].max_descriptor() > max_fd) + max_fd = fd_sets_[write_op].max_descriptor(); + fd_sets_[except_op].set(op_queue_[connect_op], ops); + if (fd_sets_[except_op].max_descriptor() > max_fd) + max_fd = fd_sets_[except_op].max_descriptor(); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!usec && !have_work_to_do) + return; + + // Determine how long to block while waiting for events. + timeval tv_buf = { 0, 0 }; + timeval* tv = usec ? get_timeout(usec, tv_buf) : &tv_buf; + + lock.unlock(); + + // Block on the select call until descriptors become ready. + asio::error_code ec; + int retval = socket_ops::select(static_cast(max_fd + 1), + fd_sets_[read_op], fd_sets_[write_op], fd_sets_[except_op], tv, ec); + + // Reset the interrupter. + if (retval > 0 && fd_sets_[read_op].is_set(interrupter_.read_descriptor())) + { + interrupter_.reset(); + --retval; + } + + lock.lock(); + + // Dispatch all ready operations. + if (retval > 0) + { +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + fd_sets_[except_op].perform(op_queue_[connect_op], ops); + fd_sets_[write_op].perform(op_queue_[connect_op], ops); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + for (int i = max_select_ops - 1; i >= 0; --i) + fd_sets_[i].perform(op_queue_[i], ops); + } + timer_queues_.get_ready_timers(ops); +} + +void select_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +#if defined(ASIO_HAS_IOCP) +void select_reactor::run_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + op_queue ops; + run(true, ops); + scheduler_.post_deferred_completions(ops); + lock.lock(); + } +} +#endif // defined(ASIO_HAS_IOCP) + +void select_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void select_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +timeval* select_reactor::get_timeout(long usec, timeval& tv) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const long max_usec = 5 * 60 * 1000 * 1000; + usec = timer_queues_.wait_duration_usec( + (usec < 0 || max_usec < usec) ? max_usec : usec); + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + return &tv; +} + +void select_reactor::cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec) +{ + bool need_interrupt = false; + op_queue ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + scheduler_.post_deferred_completions(ops); + if (need_interrupt) + interrupter_.interrupt(); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE)) + // && !defined(ASIO_WINDOWS_RUNTIME)) + +#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP diff --git a/subprojects/asio/include/asio/detail/impl/service_registry.hpp b/subprojects/asio/include/asio/detail/impl/service_registry.hpp new file mode 100644 index 000000000..d4db58994 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/service_registry.hpp @@ -0,0 +1,94 @@ +// +// detail/impl/service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +Service& service_registry::use_service() +{ + execution_context::service::key key; + init_key(key, 0); + factory_type factory = &service_registry::create; + return *static_cast(do_use_service(key, factory, &owner_)); +} + +template +Service& service_registry::use_service(io_context& owner) +{ + execution_context::service::key key; + init_key(key, 0); + factory_type factory = &service_registry::create; + return *static_cast(do_use_service(key, factory, &owner)); +} + +template +void service_registry::add_service(Service* new_service) +{ + execution_context::service::key key; + init_key(key, 0); + return do_add_service(key, new_service); +} + +template +bool service_registry::has_service() const +{ + execution_context::service::key key; + init_key(key, 0); + return do_has_service(key); +} + +template +inline void service_registry::init_key( + execution_context::service::key& key, ...) +{ + init_key_from_id(key, Service::id); +} + +#if !defined(ASIO_NO_TYPEID) +template +void service_registry::init_key(execution_context::service::key& key, + typename enable_if< + is_base_of::value>::type*) +{ + key.type_info_ = &typeid(typeid_wrapper); + key.id_ = 0; +} + +template +void service_registry::init_key_from_id(execution_context::service::key& key, + const service_id& /*id*/) +{ + key.type_info_ = &typeid(typeid_wrapper); + key.id_ = 0; +} +#endif // !defined(ASIO_NO_TYPEID) + +template +execution_context::service* service_registry::create(void* owner) +{ + return new Service(*static_cast(owner)); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP diff --git a/subprojects/asio/include/asio/detail/impl/service_registry.ipp b/subprojects/asio/include/asio/detail/impl/service_registry.ipp new file mode 100644 index 000000000..a46503321 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/service_registry.ipp @@ -0,0 +1,197 @@ +// +// detail/impl/service_registry.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP +#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/service_registry.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +service_registry::service_registry(execution_context& owner) + : owner_(owner), + first_service_(0) +{ +} + +service_registry::~service_registry() +{ +} + +void service_registry::shutdown_services() +{ + execution_context::service* service = first_service_; + while (service) + { + service->shutdown(); + service = service->next_; + } +} + +void service_registry::destroy_services() +{ + while (first_service_) + { + execution_context::service* next_service = first_service_->next_; + destroy(first_service_); + first_service_ = next_service; + } +} + +void service_registry::notify_fork(execution_context::fork_event fork_ev) +{ + // Make a copy of all of the services while holding the lock. We don't want + // to hold the lock while calling into each service, as it may try to call + // back into this class. + std::vector services; + { + asio::detail::mutex::scoped_lock lock(mutex_); + execution_context::service* service = first_service_; + while (service) + { + services.push_back(service); + service = service->next_; + } + } + + // If processing the fork_prepare event, we want to go in reverse order of + // service registration, which happens to be the existing order of the + // services in the vector. For the other events we want to go in the other + // direction. + std::size_t num_services = services.size(); + if (fork_ev == execution_context::fork_prepare) + for (std::size_t i = 0; i < num_services; ++i) + services[i]->notify_fork(fork_ev); + else + for (std::size_t i = num_services; i > 0; --i) + services[i - 1]->notify_fork(fork_ev); +} + +void service_registry::init_key_from_id(execution_context::service::key& key, + const execution_context::id& id) +{ + key.type_info_ = 0; + key.id_ = &id; +} + +bool service_registry::keys_match( + const execution_context::service::key& key1, + const execution_context::service::key& key2) +{ + if (key1.id_ && key2.id_) + if (key1.id_ == key2.id_) + return true; + if (key1.type_info_ && key2.type_info_) + if (*key1.type_info_ == *key2.type_info_) + return true; + return false; +} + +void service_registry::destroy(execution_context::service* service) +{ + delete service; +} + +execution_context::service* service_registry::do_use_service( + const execution_context::service::key& key, + factory_type factory, void* owner) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // First see if there is an existing service object with the given key. + execution_context::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Create a new service object. The service registry's mutex is not locked + // at this time to allow for nested calls into this function from the new + // service's constructor. + lock.unlock(); + auto_service_ptr new_service = { factory(owner) }; + new_service.ptr_->key_ = key; + lock.lock(); + + // Check that nobody else created another service object of the same type + // while the lock was released. + service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Service was successfully initialised, pass ownership to registry. + new_service.ptr_->next_ = first_service_; + first_service_ = new_service.ptr_; + new_service.ptr_ = 0; + return first_service_; +} + +void service_registry::do_add_service( + const execution_context::service::key& key, + execution_context::service* new_service) +{ + if (&owner_ != &new_service->context()) + asio::detail::throw_exception(invalid_service_owner()); + + asio::detail::mutex::scoped_lock lock(mutex_); + + // Check if there is an existing service object with the given key. + execution_context::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + asio::detail::throw_exception(service_already_exists()); + service = service->next_; + } + + // Take ownership of the service object. + new_service->key_ = key; + new_service->next_ = first_service_; + first_service_ = new_service; +} + +bool service_registry::do_has_service( + const execution_context::service::key& key) const +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + execution_context::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return true; + service = service->next_; + } + + return false; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP diff --git a/subprojects/asio/include/asio/detail/impl/signal_set_service.ipp b/subprojects/asio/include/asio/detail/impl/signal_set_service.ipp new file mode 100644 index 000000000..dd68fc152 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/signal_set_service.ipp @@ -0,0 +1,669 @@ +// +// detail/impl/signal_set_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP +#define ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include "asio/detail/reactor.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/signal_set_service.hpp" +#include "asio/detail/static_mutex.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct signal_state +{ + // Mutex used for protecting global state. + static_mutex mutex_; + + // The read end of the pipe used for signal notifications. + int read_descriptor_; + + // The write end of the pipe used for signal notifications. + int write_descriptor_; + + // Whether the signal state has been prepared for a fork. + bool fork_prepared_; + + // The head of a linked list of all signal_set_service instances. + class signal_set_service* service_list_; + + // A count of the number of objects that are registered for each signal. + std::size_t registration_count_[max_signal_number]; +}; + +signal_state* get_signal_state() +{ + static signal_state state = { + ASIO_STATIC_MUTEX_INIT, -1, -1, false, 0, { 0 } }; + return &state; +} + +void asio_signal_handler(int signal_number) +{ +#if defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) + signal_set_service::deliver_signal(signal_number); +#else // defined(ASIO_WINDOWS) + // || defined(ASIO_WINDOWS_RUNTIME) + // || defined(__CYGWIN__) + int saved_errno = errno; + signal_state* state = get_signal_state(); + signed_size_type result = ::write(state->write_descriptor_, + &signal_number, sizeof(signal_number)); + (void)result; + errno = saved_errno; +#endif // defined(ASIO_WINDOWS) + // || defined(ASIO_WINDOWS_RUNTIME) + // || defined(__CYGWIN__) + +#if defined(ASIO_HAS_SIGNAL) && !defined(ASIO_HAS_SIGACTION) + ::signal(signal_number, asio_signal_handler); +#endif // defined(ASIO_HAS_SIGNAL) && !defined(ASIO_HAS_SIGACTION) +} + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +class signal_set_service::pipe_read_op : public reactor_op +{ +public: + pipe_read_op() + : reactor_op(&pipe_read_op::do_perform, pipe_read_op::do_complete) + { + } + + static status do_perform(reactor_op*) + { + signal_state* state = get_signal_state(); + + int fd = state->read_descriptor_; + int signal_number = 0; + while (::read(fd, &signal_number, sizeof(int)) == sizeof(int)) + if (signal_number >= 0 && signal_number < max_signal_number) + signal_set_service::deliver_signal(signal_number); + + return not_done; + } + + static void do_complete(void* /*owner*/, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + pipe_read_op* o(static_cast(base)); + delete o; + } +}; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +signal_set_service::signal_set_service( + asio::io_context& io_context) + : service_base(io_context), + io_context_(asio::use_service(io_context)), +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + reactor_(asio::use_service(io_context)), +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + next_(0), + prev_(0) +{ + get_signal_state()->mutex_.init(); + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + reactor_.init_task(); +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + + for (int i = 0; i < max_signal_number; ++i) + registrations_[i] = 0; + + add_service(this); +} + +signal_set_service::~signal_set_service() +{ + remove_service(this); +} + +void signal_set_service::shutdown() +{ + remove_service(this); + + op_queue ops; + + for (int i = 0; i < max_signal_number; ++i) + { + registration* reg = registrations_[i]; + while (reg) + { + ops.push(*reg->queue_); + reg = reg->next_in_table_; + } + } + + io_context_.abandon_operations(ops); +} + +void signal_set_service::notify_fork( + asio::io_context::fork_event fork_ev) +{ +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + switch (fork_ev) + { + case asio::io_context::fork_prepare: + { + int read_descriptor = state->read_descriptor_; + state->fork_prepared_ = true; + lock.unlock(); + reactor_.deregister_internal_descriptor(read_descriptor, reactor_data_); + reactor_.cleanup_descriptor_data(reactor_data_); + } + break; + case asio::io_context::fork_parent: + if (state->fork_prepared_) + { + int read_descriptor = state->read_descriptor_; + state->fork_prepared_ = false; + lock.unlock(); + reactor_.register_internal_descriptor(reactor::read_op, + read_descriptor, reactor_data_, new pipe_read_op); + } + break; + case asio::io_context::fork_child: + if (state->fork_prepared_) + { + asio::detail::signal_blocker blocker; + close_descriptors(); + open_descriptors(); + int read_descriptor = state->read_descriptor_; + state->fork_prepared_ = false; + lock.unlock(); + reactor_.register_internal_descriptor(reactor::read_op, + read_descriptor, reactor_data_, new pipe_read_op); + } + break; + default: + break; + } +#else // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + (void)fork_ev; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::construct( + signal_set_service::implementation_type& impl) +{ + impl.signals_ = 0; +} + +void signal_set_service::destroy( + signal_set_service::implementation_type& impl) +{ + asio::error_code ignored_ec; + clear(impl, ignored_ec); + cancel(impl, ignored_ec); +} + +asio::error_code signal_set_service::add( + signal_set_service::implementation_type& impl, + int signal_number, asio::error_code& ec) +{ + // Check that the signal number is valid. + if (signal_number < 0 || signal_number >= max_signal_number) + { + ec = asio::error::invalid_argument; + return ec; + } + + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + // Find the appropriate place to insert the registration. + registration** insertion_point = &impl.signals_; + registration* next = impl.signals_; + while (next && next->signal_number_ < signal_number) + { + insertion_point = &next->next_in_set_; + next = next->next_in_set_; + } + + // Only do something if the signal is not already registered. + if (next == 0 || next->signal_number_ != signal_number) + { + registration* new_registration = new registration; + +#if defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + // Register for the signal if we're the first. + if (state->registration_count_[signal_number] == 0) + { +# if defined(ASIO_HAS_SIGACTION) + using namespace std; // For memset. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = asio_signal_handler; + sigfillset(&sa.sa_mask); + if (::sigaction(signal_number, &sa, 0) == -1) +# else // defined(ASIO_HAS_SIGACTION) + if (::signal(signal_number, asio_signal_handler) == SIG_ERR) +# endif // defined(ASIO_HAS_SIGACTION) + { +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error::invalid_argument; +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(errno, + asio::error::get_system_category()); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + delete new_registration; + return ec; + } + } +#endif // defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + + // Record the new registration in the set. + new_registration->signal_number_ = signal_number; + new_registration->queue_ = &impl.queue_; + new_registration->next_in_set_ = next; + *insertion_point = new_registration; + + // Insert registration into the registration table. + new_registration->next_in_table_ = registrations_[signal_number]; + if (registrations_[signal_number]) + registrations_[signal_number]->prev_in_table_ = new_registration; + registrations_[signal_number] = new_registration; + + ++state->registration_count_[signal_number]; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code signal_set_service::remove( + signal_set_service::implementation_type& impl, + int signal_number, asio::error_code& ec) +{ + // Check that the signal number is valid. + if (signal_number < 0 || signal_number >= max_signal_number) + { + ec = asio::error::invalid_argument; + return ec; + } + + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + // Find the signal number in the list of registrations. + registration** deletion_point = &impl.signals_; + registration* reg = impl.signals_; + while (reg && reg->signal_number_ < signal_number) + { + deletion_point = ®->next_in_set_; + reg = reg->next_in_set_; + } + + if (reg != 0 && reg->signal_number_ == signal_number) + { +#if defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + // Set signal handler back to the default if we're the last. + if (state->registration_count_[signal_number] == 1) + { +# if defined(ASIO_HAS_SIGACTION) + using namespace std; // For memset. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (::sigaction(signal_number, &sa, 0) == -1) +# else // defined(ASIO_HAS_SIGACTION) + if (::signal(signal_number, SIG_DFL) == SIG_ERR) +# endif // defined(ASIO_HAS_SIGACTION) + { +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error::invalid_argument; +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(errno, + asio::error::get_system_category()); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + return ec; + } + } +#endif // defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + + // Remove the registration from the set. + *deletion_point = reg->next_in_set_; + + // Remove the registration from the registration table. + if (registrations_[signal_number] == reg) + registrations_[signal_number] = reg->next_in_table_; + if (reg->prev_in_table_) + reg->prev_in_table_->next_in_table_ = reg->next_in_table_; + if (reg->next_in_table_) + reg->next_in_table_->prev_in_table_ = reg->prev_in_table_; + + --state->registration_count_[signal_number]; + + delete reg; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code signal_set_service::clear( + signal_set_service::implementation_type& impl, + asio::error_code& ec) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + while (registration* reg = impl.signals_) + { +#if defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + // Set signal handler back to the default if we're the last. + if (state->registration_count_[reg->signal_number_] == 1) + { +# if defined(ASIO_HAS_SIGACTION) + using namespace std; // For memset. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (::sigaction(reg->signal_number_, &sa, 0) == -1) +# else // defined(ASIO_HAS_SIGACTION) + if (::signal(reg->signal_number_, SIG_DFL) == SIG_ERR) +# endif // defined(ASIO_HAS_SIGACTION) + { +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error::invalid_argument; +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(errno, + asio::error::get_system_category()); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + return ec; + } + } +#endif // defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + + // Remove the registration from the registration table. + if (registrations_[reg->signal_number_] == reg) + registrations_[reg->signal_number_] = reg->next_in_table_; + if (reg->prev_in_table_) + reg->prev_in_table_->next_in_table_ = reg->next_in_table_; + if (reg->next_in_table_) + reg->next_in_table_->prev_in_table_ = reg->prev_in_table_; + + --state->registration_count_[reg->signal_number_]; + + impl.signals_ = reg->next_in_set_; + delete reg; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code signal_set_service::cancel( + signal_set_service::implementation_type& impl, + asio::error_code& ec) +{ + ASIO_HANDLER_OPERATION((io_context_.context(), + "signal_set", &impl, 0, "cancel")); + + op_queue ops; + { + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + while (signal_op* op = impl.queue_.front()) + { + op->ec_ = asio::error::operation_aborted; + impl.queue_.pop(); + ops.push(op); + } + } + + io_context_.post_deferred_completions(ops); + + ec = asio::error_code(); + return ec; +} + +void signal_set_service::deliver_signal(int signal_number) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + signal_set_service* service = state->service_list_; + while (service) + { + op_queue ops; + + registration* reg = service->registrations_[signal_number]; + while (reg) + { + if (reg->queue_->empty()) + { + ++reg->undelivered_; + } + else + { + while (signal_op* op = reg->queue_->front()) + { + op->signal_number_ = signal_number; + reg->queue_->pop(); + ops.push(op); + } + } + + reg = reg->next_in_table_; + } + + service->io_context_.post_deferred_completions(ops); + + service = service->next_; + } +} + +void signal_set_service::add_service(signal_set_service* service) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + // If this is the first service to be created, open a new pipe. + if (state->service_list_ == 0) + open_descriptors(); +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + + // If an io_context object is thread-unsafe then it must be the only + // io_context used to create signal_set objects. + if (state->service_list_ != 0) + { + if (!ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER, + service->io_context_.concurrency_hint()) + || !ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER, + state->service_list_->io_context_.concurrency_hint())) + { + std::logic_error ex( + "Thread-unsafe io_context objects require " + "exclusive access to signal handling."); + asio::detail::throw_exception(ex); + } + } + + // Insert service into linked list of all services. + service->next_ = state->service_list_; + service->prev_ = 0; + if (state->service_list_) + state->service_list_->prev_ = service; + state->service_list_ = service; + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + // Register for pipe readiness notifications. + int read_descriptor = state->read_descriptor_; + lock.unlock(); + service->reactor_.register_internal_descriptor(reactor::read_op, + read_descriptor, service->reactor_data_, new pipe_read_op); +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::remove_service(signal_set_service* service) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + if (service->next_ || service->prev_ || state->service_list_ == service) + { +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + // Disable the pipe readiness notifications. + int read_descriptor = state->read_descriptor_; + lock.unlock(); + service->reactor_.deregister_internal_descriptor( + read_descriptor, service->reactor_data_); + service->reactor_.cleanup_descriptor_data(service->reactor_data_); + lock.lock(); +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + + // Remove service from linked list of all services. + if (state->service_list_ == service) + state->service_list_ = service->next_; + if (service->prev_) + service->prev_->next_ = service->next_; + if (service->next_) + service->next_->prev_= service->prev_; + service->next_ = 0; + service->prev_ = 0; + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + // If this is the last service to be removed, close the pipe. + if (state->service_list_ == 0) + close_descriptors(); +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + } +} + +void signal_set_service::open_descriptors() +{ +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + + int pipe_fds[2]; + if (::pipe(pipe_fds) == 0) + { + state->read_descriptor_ = pipe_fds[0]; + ::fcntl(state->read_descriptor_, F_SETFL, O_NONBLOCK); + + state->write_descriptor_ = pipe_fds[1]; + ::fcntl(state->write_descriptor_, F_SETFL, O_NONBLOCK); + +#if defined(FD_CLOEXEC) + ::fcntl(state->read_descriptor_, F_SETFD, FD_CLOEXEC); + ::fcntl(state->write_descriptor_, F_SETFD, FD_CLOEXEC); +#endif // defined(FD_CLOEXEC) + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "signal_set_service pipe"); + } +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::close_descriptors() +{ +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + + if (state->read_descriptor_ != -1) + ::close(state->read_descriptor_); + state->read_descriptor_ = -1; + + if (state->write_descriptor_ != -1) + ::close(state->write_descriptor_); + state->write_descriptor_ = -1; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::start_wait_op( + signal_set_service::implementation_type& impl, signal_op* op) +{ + io_context_.work_started(); + + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + registration* reg = impl.signals_; + while (reg) + { + if (reg->undelivered_ > 0) + { + --reg->undelivered_; + op->signal_number_ = reg->signal_number_; + io_context_.post_deferred_completion(op); + return; + } + + reg = reg->next_in_set_; + } + + impl.queue_.push(op); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/socket_ops.ipp b/subprojects/asio/include/asio/detail/impl/socket_ops.ipp new file mode 100644 index 000000000..7df3400f7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/socket_ops.ipp @@ -0,0 +1,3571 @@ +// +// detail/impl/socket_ops.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPS_IPP +#define ASIO_DETAIL_SOCKET_OPS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include +#include +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include +# include +# include +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) \ + || defined(__MACH__) && defined(__APPLE__) +# if defined(ASIO_HAS_PTHREADS) +# include +# endif // defined(ASIO_HAS_PTHREADS) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // || defined(__MACH__) && defined(__APPLE__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +struct msghdr { int msg_namelen; }; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) +// HP-UX doesn't declare these functions extern "C", so they are declared again +// here to avoid linker errors about undefined symbols. +extern "C" char* if_indextoname(unsigned int, char*); +extern "C" unsigned int if_nametoindex(const char*); +#endif // defined(__hpux) + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +inline void clear_last_error() +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + WSASetLastError(0); +#else + errno = 0; +#endif +} + +#if !defined(ASIO_WINDOWS_RUNTIME) + +template +inline ReturnType error_wrapper(ReturnType return_value, + asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(WSAGetLastError(), + asio::error::get_system_category()); +#else + ec = asio::error_code(errno, + asio::error::get_system_category()); +#endif + return return_value; +} + +template +inline socket_type call_accept(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; + socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0); + if (addrlen) + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +socket_type accept(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return invalid_socket; + } + + clear_last_error(); + + socket_type new_s = error_wrapper(call_accept( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (new_s == invalid_socket) + return new_s; + +#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + int optval = 1; + int result = error_wrapper(::setsockopt(new_s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(new_s); + return invalid_socket; + } +#endif + + ec = asio::error_code(); + return new_s; +} + +socket_type sync_accept(socket_type s, state_type state, + socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) +{ + // Accept a socket. + for (;;) + { + // Try to complete the operation without blocking. + socket_type new_socket = socket_ops::accept(s, addr, addrlen, ec); + + // Check if operation succeeded. + if (new_socket != invalid_socket) + return new_socket; + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + if (state & user_set_non_blocking) + return invalid_socket; + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (state & enable_connection_aborted) + return invalid_socket; + // Fall through to retry operation. + } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (state & enable_connection_aborted) + return invalid_socket; + // Fall through to retry operation. + } +#endif // defined(EPROTO) + else + return invalid_socket; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return invalid_socket; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_accept(socket_type s, + void* output_buffer, DWORD address_length, + socket_addr_type* addr, std::size_t* addrlen, + socket_type new_socket, asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_aborted; + + if (!ec) + { + // Get the address of the peer. + if (addr && addrlen) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(output_buffer, 0, address_length, + address_length, &local_addr, &local_addr_length, + &remote_addr, &remote_addr_length); + if (static_cast(remote_addr_length) > *addrlen) + { + ec = asio::error::invalid_argument; + } + else + { + using namespace std; // For memcpy. + memcpy(addr, remote_addr, remote_addr_length); + *addrlen = static_cast(remote_addr_length); + } + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + SOCKET update_ctx_param = s; + socket_ops::state_type state = 0; + socket_ops::setsockopt(new_socket, state, + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET), ec); + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_accept(socket_type s, + state_type state, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, socket_type& new_socket) +{ + for (;;) + { + // Accept the waiting connection. + new_socket = socket_ops::accept(s, addr, addrlen, ec); + + // Check if operation succeeded. + if (new_socket != invalid_socket) + return true; + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (state & enable_connection_aborted) + return true; + // Fall through to retry operation. + } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (state & enable_connection_aborted) + return true; + // Fall through to retry operation. + } +#endif // defined(EPROTO) + else + return true; + + return false; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +template +inline int call_bind(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::bind(s, addr, (SockLenType)addrlen); +} + +int bind(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(call_bind( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +int close(socket_type s, state_type& state, + bool destruction, asio::error_code& ec) +{ + int result = 0; + if (s != invalid_socket) + { + // We don't want the destructor to block, so set the socket to linger in + // the background. If the user doesn't like this behaviour then they need + // to explicitly close the socket. + if (destruction && (state & user_set_linger)) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + asio::error_code ignored_ec; + socket_ops::setsockopt(s, state, SOL_SOCKET, + SO_LINGER, &opt, sizeof(opt), ignored_ec); + } + + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = error_wrapper(::closesocket(s), ec); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = error_wrapper(::close(s), ec); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + + if (result != 0 + && (ec == asio::error::would_block + || ec == asio::error::try_again)) + { + // According to UNIX Network Programming Vol. 1, it is possible for + // close() to fail with EWOULDBLOCK under certain circumstances. What + // isn't clear is the state of the descriptor after this error. The one + // current OS where this behaviour is seen, Windows, says that the socket + // remains open. Therefore we'll put the descriptor back into blocking + // mode and have another attempt at closing it. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = 0; + ::ioctlsocket(s, FIONBIO, &arg); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(__SYMBIAN32__) + int flags = ::fcntl(s, F_GETFL, 0); + if (flags >= 0) + ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); +# else // defined(__SYMBIAN32__) + ioctl_arg_type arg = 0; + ::ioctl(s, FIONBIO, &arg); +# endif // defined(__SYMBIAN32__) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + state &= ~non_blocking; + + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = error_wrapper(::closesocket(s), ec); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = error_wrapper(::close(s), ec); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + } + } + + if (result == 0) + ec = asio::error_code(); + return result; +} + +bool set_user_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = error_wrapper(::ioctlsocket(s, FIONBIO, &arg), ec); +#elif defined(__SYMBIAN32__) + int result = error_wrapper(::fcntl(s, F_GETFL, 0), ec); + if (result >= 0) + { + clear_last_error(); + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = error_wrapper(::fcntl(s, F_SETFL, flag), ec); + } +#else + ioctl_arg_type arg = (value ? 1 : 0); + int result = error_wrapper(::ioctl(s, FIONBIO, &arg), ec); +#endif + + if (result >= 0) + { + ec = asio::error_code(); + if (value) + state |= user_set_non_blocking; + else + { + // Clearing the user-set non-blocking mode always overrides any + // internally-set non-blocking flag. Any subsequent asynchronous + // operations will need to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + return true; + } + + return false; +} + +bool set_internal_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + + if (!value && (state & user_set_non_blocking)) + { + // It does not make sense to clear the internal non-blocking flag if the + // user still wants non-blocking behaviour. Return an error and let the + // caller figure out whether to update the user-set non-blocking flag. + ec = asio::error::invalid_argument; + return false; + } + + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = error_wrapper(::ioctlsocket(s, FIONBIO, &arg), ec); +#elif defined(__SYMBIAN32__) + int result = error_wrapper(::fcntl(s, F_GETFL, 0), ec); + if (result >= 0) + { + clear_last_error(); + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = error_wrapper(::fcntl(s, F_SETFL, flag), ec); + } +#else + ioctl_arg_type arg = (value ? 1 : 0); + int result = error_wrapper(::ioctl(s, FIONBIO, &arg), ec); +#endif + + if (result >= 0) + { + ec = asio::error_code(); + if (value) + state |= internal_non_blocking; + else + state &= ~internal_non_blocking; + return true; + } + + return false; +} + +int shutdown(socket_type s, int what, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(::shutdown(s, what), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +template +inline int call_connect(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::connect(s, addr, (SockLenType)addrlen); +} + +int connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(call_connect( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); +#if defined(__linux__) + else if (ec == asio::error::try_again) + ec = asio::error::no_buffer_space; +#endif // defined(__linux__) + return result; +} + +void sync_connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + // Perform the connect operation. + socket_ops::connect(s, addr, addrlen, ec); + if (ec != asio::error::in_progress + && ec != asio::error::would_block) + { + // The connect operation finished immediately. + return; + } + + // Wait for socket to become ready. + if (socket_ops::poll_connect(s, -1, ec) < 0) + return; + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == socket_error_retval) + return; + + // Return the result of the connect operation. + ec = asio::error_code(connect_error, + asio::error::get_system_category()); +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_connect(socket_type s, asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + switch (ec.value()) + { + case ERROR_CONNECTION_REFUSED: + ec = asio::error::connection_refused; + break; + case ERROR_NETWORK_UNREACHABLE: + ec = asio::error::network_unreachable; + break; + case ERROR_HOST_UNREACHABLE: + ec = asio::error::host_unreachable; + break; + case ERROR_SEM_TIMEOUT: + ec = asio::error::timed_out; + break; + default: + break; + } + + if (!ec) + { + // Need to set the SO_UPDATE_CONNECT_CONTEXT option so that getsockname + // and getpeername will work on the connected socket. + socket_ops::state_type state = 0; + const int so_update_connect_context = 0x7010; + socket_ops::setsockopt(s, state, SOL_SOCKET, + so_update_connect_context, 0, 0, ec); + } +} + +#endif // defined(ASIO_HAS_IOCP) + +bool non_blocking_connect(socket_type s, asio::error_code& ec) +{ + // Check if the connect operation has finished. This is required since we may + // get spurious readiness notifications from the reactor. +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set write_fds; + FD_ZERO(&write_fds); + FD_SET(s, &write_fds); + fd_set except_fds; + FD_ZERO(&except_fds); + FD_SET(s, &except_fds); + timeval zero_timeout; + zero_timeout.tv_sec = 0; + zero_timeout.tv_usec = 0; + int ready = ::select(s + 1, 0, &write_fds, &except_fds, &zero_timeout); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + int ready = ::poll(&fds, 1, 0); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (ready == 0) + { + // The asynchronous connect operation is still in progress. + return false; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == 0) + { + if (connect_error) + { + ec = asio::error_code(connect_error, + asio::error::get_system_category()); + } + else + ec = asio::error_code(); + } + + return true; +} + +int socketpair(int af, int type, int protocol, + socket_type sv[2], asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + (void)(af); + (void)(type); + (void)(protocol); + (void)(sv); + ec = asio::error::operation_not_supported; + return socket_error_retval; +#else + clear_last_error(); + int result = error_wrapper(::socketpair(af, type, protocol, sv), ec); + if (result == 0) + ec = asio::error_code(); + return result; +#endif +} + +bool sockatmark(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + +#if defined(SIOCATMARK) + ioctl_arg_type value = 0; +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctlsocket(s, SIOCATMARK, &value), ec); +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctl(s, SIOCATMARK, &value), ec); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + if (result == 0) + ec = asio::error_code(); +# if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +# endif // defined(ENOTTY) +#else // defined(SIOCATMARK) + int value = error_wrapper(::sockatmark(s), ec); + if (value != -1) + ec = asio::error_code(); +#endif // defined(SIOCATMARK) + + return ec ? false : value != 0; +} + +size_t available(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + ioctl_arg_type value = 0; +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctlsocket(s, FIONREAD, &value), ec); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctl(s, FIONREAD, &value), ec); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + if (result == 0) + ec = asio::error_code(); +#if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +#endif // defined(ENOTTY) + + return ec ? static_cast(0) : static_cast(value); +} + +int listen(socket_type s, int backlog, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(::listen(s, backlog), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +inline void init_buf_iov_base(void*& base, void* addr) +{ + base = addr; +} + +template +inline void init_buf_iov_base(T& base, void* addr) +{ + base = static_cast(addr); +} + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +void init_buf(buf& b, void* data, size_t size) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(data); + b.len = static_cast(size); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, data); + b.iov_len = size; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +void init_buf(buf& b, const void* data, size_t size) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(const_cast(data)); + b.len = static_cast(size); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, const_cast(data)); + b.iov_len = size; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr) +{ + name = addr; +} + +inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) +{ + name = const_cast(addr); +} + +template +inline void init_msghdr_msg_name(T& name, socket_addr_type* addr) +{ + name = reinterpret_cast(addr); +} + +template +inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr) +{ + name = reinterpret_cast(const_cast(addr)); +} + +signed_size_type recv(socket_type s, buf* bufs, size_t count, + int flags, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecv(s, bufs, + recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + ec.assign(0, ec.category()); + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast(count); + signed_size_type result = error_wrapper(::recvmsg(s, &msg, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recv(socket_type s, state_type state, buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (all_empty && (state & stream_oriented)) + { + ec = asio::error_code(); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Check for EOF. + if ((state & stream_oriented) && bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_recv(state_type state, + const weak_cancel_token_type& cancel_token, bool all_empty, + asio::error_code& ec, size_t bytes_transferred) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + { + ec.assign(0, ec.category()); + } + + // Check for connection closed. + else if (!ec && bytes_transferred == 0 + && (state & stream_oriented) != 0 + && !all_empty) + { + ec = asio::error::eof; + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); + + // Check for end of stream. + if (is_stream && bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type recvfrom(socket_type s, buf* bufs, size_t count, + int flags, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int tmp_addrlen = (int)*addrlen; + int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec); + *addrlen = (std::size_t)tmp_addrlen; + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + ec.assign(0, ec.category()); + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = static_cast(*addrlen); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast(count); + signed_size_type result = error_wrapper(::recvmsg(s, &msg, flags), ec); + *addrlen = msg.msg_namelen; + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + { + ec.assign(0, ec.category()); + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type recvmsg(socket_type s, buf* bufs, size_t count, + int in_flags, int& out_flags, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + out_flags = 0; + return socket_ops::recv(s, bufs, count, in_flags, ec); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast(count); + signed_size_type result = error_wrapper(::recvmsg(s, &msg, in_flags), ec); + if (result >= 0) + { + ec = asio::error_code(); + out_flags = msg.msg_flags; + } + else + out_flags = 0; + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recvmsg(socket_type s, state_type state, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recvmsg( + s, bufs, count, in_flags, out_flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_recvmsg( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + { + ec.assign(0, ec.category()); + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_recvmsg(socket_type s, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recvmsg( + s, bufs, count, in_flags, out_flags, ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type send(socket_type s, const buf* bufs, size_t count, + int flags, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = error_wrapper(::WSASend(s, const_cast(bufs), + send_buf_count, &bytes_transferred, send_flags, 0, 0), ec); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = static_cast(count); +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + signed_size_type result = error_wrapper(::sendmsg(s, &msg, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_send(socket_type s, state_type state, const buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes to a stream is a no-op. + if (all_empty && (state & stream_oriented)) + { + ec = asio::error_code(); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_send( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_send(socket_type s, + const buf* bufs, size_t count, int flags, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type sendto(socket_type s, const buf* bufs, size_t count, + int flags, const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + int result = error_wrapper(::WSASendTo(s, const_cast(bufs), + send_buf_count, &bytes_transferred, flags, addr, + static_cast(addrlen), 0, 0), ec); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = static_cast(addrlen); + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = static_cast(count); +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + signed_size_type result = error_wrapper(::sendmsg(s, &msg, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_sendto(socket_type s, state_type state, const buf* bufs, + size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::sendto( + s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, 0, -1, ec) < 0) + return 0; + } +} + +#if !defined(ASIO_HAS_IOCP) + +bool non_blocking_sendto(socket_type s, + const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = socket_ops::sendto( + s, bufs, count, flags, addr, addrlen, ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // !defined(ASIO_HAS_IOCP) + +socket_type socket(int af, int type, int protocol, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + socket_type s = error_wrapper(::WSASocketW(af, type, protocol, 0, 0, + WSA_FLAG_OVERLAPPED), ec); + if (s == invalid_socket) + return s; + + if (af == ASIO_OS_DEF(AF_INET6)) + { + // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to + // false. This will only succeed on Windows Vista and later versions of + // Windows, where a dual-stack IPv4/v6 implementation is available. + DWORD optval = 0; + ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&optval), sizeof(optval)); + } + + ec = asio::error_code(); + + return s; +#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + socket_type s = error_wrapper(::socket(af, type, protocol), ec); + if (s == invalid_socket) + return s; + + int optval = 1; + int result = error_wrapper(::setsockopt(s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(s); + return invalid_socket; + } + + return s; +#else + int s = error_wrapper(::socket(af, type, protocol), ec); + if (s >= 0) + ec = asio::error_code(); + return s; +#endif +} + +template +inline int call_setsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + const void* optval, std::size_t optlen) +{ + return ::setsockopt(s, level, optname, + (const char*)optval, (SockLenType)optlen); +} + +int setsockopt(socket_type s, state_type& state, int level, int optname, + const void* optval, std::size_t optlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (level == custom_socket_option_level + && optname == enable_connection_aborted_option) + { + if (optlen != sizeof(int)) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (*static_cast(optval)) + state |= enable_connection_aborted; + else + state &= ~enable_connection_aborted; + ec = asio::error_code(); + return 0; + } + + if (level == SOL_SOCKET && optname == SO_LINGER) + state |= user_set_linger; + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); + if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) + { + clear_last_error(); + return error_wrapper(sso(s, level, optname, + reinterpret_cast(optval), + static_cast(optlen)), ec); + } + } + ec = asio::error::fault; + return socket_error_retval; +#else // defined(__BORLANDC__) + clear_last_error(); + int result = error_wrapper(call_setsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); + if (result == 0) + { + ec = asio::error_code(); + +#if defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + // To implement portable behaviour for SO_REUSEADDR with UDP sockets we + // need to also set SO_REUSEPORT on BSD-based platforms. + if ((state & datagram_oriented) + && level == SOL_SOCKET && optname == SO_REUSEADDR) + { + call_setsockopt(&msghdr::msg_namelen, s, + SOL_SOCKET, SO_REUSEPORT, optval, optlen); + } +#endif + } + + return result; +#endif // defined(__BORLANDC__) +} + +template +inline int call_getsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + void* optval, std::size_t* optlen) +{ + SockLenType tmp_optlen = (SockLenType)*optlen; + int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); + *optlen = (std::size_t)tmp_optlen; + return result; +} + +int getsockopt(socket_type s, state_type state, int level, int optname, + void* optval, size_t* optlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (level == custom_socket_option_level + && optname == enable_connection_aborted_option) + { + if (*optlen != sizeof(int)) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + *static_cast(optval) = (state & enable_connection_aborted) ? 1 : 0; + ec = asio::error_code(); + return 0; + } + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); + if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) + { + clear_last_error(); + int tmp_optlen = static_cast(*optlen); + int result = error_wrapper(gso(s, level, optname, + reinterpret_cast(optval), &tmp_optlen), ec); + *optlen = static_cast(tmp_optlen); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are + // only supported on Windows Vista and later. To simplify program logic + // we will fake success of getting this option and specify that the + // value is non-zero (i.e. true). This corresponds to the behavior of + // IPv6 sockets on Windows platforms pre-Vista. + *static_cast(optval) = 1; + ec = asio::error_code(); + } + return result; + } + } + ec = asio::error::fault; + return socket_error_retval; +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + clear_last_error(); + int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only + // supported on Windows Vista and later. To simplify program logic we will + // fake success of getting this option and specify that the value is + // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets + // on Windows platforms pre-Vista. + *static_cast(optval) = 1; + ec = asio::error_code(); + } + if (result == 0) + ec = asio::error_code(); + return result; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + clear_last_error(); + int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); +#if defined(__linux__) + if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int) + && (optname == SO_SNDBUF || optname == SO_RCVBUF)) + { + // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel + // to set the buffer size to N*2. Linux puts additional stuff into the + // buffers so that only about half is actually available to the application. + // The retrieved value is divided by 2 here to make it appear as though the + // correct value has been set. + *static_cast(optval) /= 2; + } +#endif // defined(__linux__) + if (result == 0) + ec = asio::error_code(); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +template +inline int call_getpeername(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getpeername(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +int getpeername(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, bool cached, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) && !defined(ASIO_WINDOWS_APP) \ + || defined(__CYGWIN__) + if (cached) + { + // Check if socket is still connected. + DWORD connect_time = 0; + size_t connect_time_len = sizeof(connect_time); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_CONNECT_TIME, + &connect_time, &connect_time_len, ec) == socket_error_retval) + { + return socket_error_retval; + } + if (connect_time == 0xFFFFFFFF) + { + ec = asio::error::not_connected; + return socket_error_retval; + } + + // The cached value is still valid. + ec = asio::error_code(); + return 0; + } +#else // defined(ASIO_WINDOWS) && !defined(ASIO_WINDOWS_APP) + // || defined(__CYGWIN__) + (void)cached; +#endif // defined(ASIO_WINDOWS) && !defined(ASIO_WINDOWS_APP) + // || defined(__CYGWIN__) + + clear_last_error(); + int result = error_wrapper(call_getpeername( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +template +inline int call_getsockname(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getsockname(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +int getsockname(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(call_getsockname( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +int ioctl(socket_type s, state_type& state, int cmd, + ioctl_arg_type* arg, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctlsocket(s, cmd, arg), ec); +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + int result = error_wrapper(::ioctl(s, + static_cast(cmd), arg), ec); +#else + int result = error_wrapper(::ioctl(s, cmd, arg), ec); +#endif + if (result >= 0) + { + ec = asio::error_code(); + + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the socket is already in + // the correct state. This ensures that the underlying socket is put into + // the state that has been requested by the user. If the ioctl syscall was + // successful then we need to update the flags to match. + if (cmd == static_cast(FIONBIO)) + { + if (*arg) + { + state |= user_set_non_blocking; + } + else + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + } + } + + return result; +} + +int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + if (!readfds && !writefds && !exceptfds && timeout) + { + DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + if (milliseconds == 0) + milliseconds = 1; // Force context switch. + ::Sleep(milliseconds); + ec = asio::error_code(); + return 0; + } + + // The select() call allows timeout values measured in microseconds, but the + // system clock (as wrapped by boost::posix_time::microsec_clock) typically + // has a resolution of 10 milliseconds. This can lead to a spinning select + // reactor, meaning increased CPU usage, when waiting for the earliest + // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight + // spin we'll use a minimum timeout of 1 millisecond. + if (timeout && timeout->tv_sec == 0 + && timeout->tv_usec > 0 && timeout->tv_usec < 1000) + timeout->tv_usec = 1000; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) && defined(__SELECT) + timespec ts; + ts.tv_sec = timeout ? timeout->tv_sec : 0; + ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; + return error_wrapper(::pselect(nfds, readfds, + writefds, exceptfds, timeout ? &ts : 0, 0), ec); +#else + int result = error_wrapper(::select(nfds, readfds, + writefds, exceptfds, timeout), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif +} + +int poll_read(socket_type s, state_type state, + int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + timeval timeout_obj; + timeval* timeout; + if (state & user_set_non_blocking) + { + timeout_obj.tv_sec = 0; + timeout_obj.tv_usec = 0; + timeout = &timeout_obj; + } + else if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + clear_last_error(); + int result = error_wrapper(::select(s + 1, &fds, 0, 0, timeout), ec); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLIN; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : msec; + clear_last_error(); + int result = error_wrapper(::poll(&fds, 1, timeout), ec); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (result == 0) + ec = (state & user_set_non_blocking) + ? asio::error::would_block : asio::error_code(); + else if (result > 0) + ec = asio::error_code(); + return result; +} + +int poll_write(socket_type s, state_type state, + int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + timeval timeout_obj; + timeval* timeout; + if (state & user_set_non_blocking) + { + timeout_obj.tv_sec = 0; + timeout_obj.tv_usec = 0; + timeout = &timeout_obj; + } + else if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + clear_last_error(); + int result = error_wrapper(::select(s + 1, 0, &fds, 0, timeout), ec); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : msec; + clear_last_error(); + int result = error_wrapper(::poll(&fds, 1, timeout), ec); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (result == 0) + ec = (state & user_set_non_blocking) + ? asio::error::would_block : asio::error_code(); + else if (result > 0) + ec = asio::error_code(); + return result; +} + +int poll_error(socket_type s, state_type state, + int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + timeval timeout_obj; + timeval* timeout; + if (state & user_set_non_blocking) + { + timeout_obj.tv_sec = 0; + timeout_obj.tv_usec = 0; + timeout = &timeout_obj; + } + else if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + clear_last_error(); + int result = error_wrapper(::select(s + 1, 0, 0, &fds, timeout), ec); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLPRI | POLLERR | POLLHUP; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : msec; + clear_last_error(); + int result = error_wrapper(::poll(&fds, 1, timeout), ec); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (result == 0) + ec = (state & user_set_non_blocking) + ? asio::error::would_block : asio::error_code(); + else if (result > 0) + ec = asio::error_code(); + return result; +} + +int poll_connect(socket_type s, int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set write_fds; + FD_ZERO(&write_fds); + FD_SET(s, &write_fds); + fd_set except_fds; + FD_ZERO(&except_fds); + FD_SET(s, &except_fds); + timeval timeout_obj; + timeval* timeout; + if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + clear_last_error(); + int result = error_wrapper(::select( + s + 1, 0, &write_fds, &except_fds, timeout), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + clear_last_error(); + int result = error_wrapper(::poll(&fds, 1, msec), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) +} + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +const char* inet_ntop(int af, const void* src, char* dest, size_t length, + unsigned long scope_id, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS_RUNTIME) + using namespace std; // For sprintf. + const unsigned char* bytes = static_cast(src); + if (af == ASIO_OS_DEF(AF_INET)) + { + sprintf_s(dest, length, "%u.%u.%u.%u", + bytes[0], bytes[1], bytes[2], bytes[3]); + return dest; + } + else if (af == ASIO_OS_DEF(AF_INET6)) + { + size_t n = 0, b = 0, z = 0; + while (n < length && b < 16) + { + if (bytes[b] == 0 && bytes[b + 1] == 0 && z == 0) + { + do b += 2; while (b < 16 && bytes[b] == 0 && bytes[b + 1] == 0); + n += sprintf_s(dest + n, length - n, ":%s", b < 16 ? "" : ":"), ++z; + } + else + { + n += sprintf_s(dest + n, length - n, "%s%x", b ? ":" : "", + (static_cast(bytes[b]) << 8) | bytes[b + 1]); + b += 2; + } + } + if (scope_id) + n += sprintf_s(dest + n, length - n, "%%%lu", scope_id); + return dest; + } + else + { + ec = asio::error::address_family_not_supported; + return 0; + } +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy. + + if (af != ASIO_OS_DEF(AF_INET) && af != ASIO_OS_DEF(AF_INET6)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + DWORD address_length; + if (af == ASIO_OS_DEF(AF_INET)) + { + address_length = sizeof(sockaddr_in4_type); + address.v4.sin_family = ASIO_OS_DEF(AF_INET); + address.v4.sin_port = 0; + memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); + } + else // AF_INET6 + { + address_length = sizeof(sockaddr_in6_type); + address.v6.sin6_family = ASIO_OS_DEF(AF_INET6); + address.v6.sin6_port = 0; + address.v6.sin6_flowinfo = 0; + address.v6.sin6_scope_id = scope_id; + memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); + } + + DWORD string_length = static_cast(length); +#if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) + LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); + int result = error_wrapper(::WSAAddressToStringW(&address.base, + address_length, 0, string_buffer, &string_length), ec); + ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, + dest, static_cast(length), 0, 0); +#else + int result = error_wrapper(::WSAAddressToStringA( + &address.base, address_length, 0, dest, &string_length), ec); +#endif + + // Windows may set error code on success. + if (result != socket_error_retval) + ec = asio::error_code(); + + // Windows may not set an error code on failure. + else if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + return result == socket_error_retval ? 0 : dest; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + const char* result = error_wrapper(::inet_ntop( + af, src, dest, static_cast(length)), ec); + if (result == 0 && !ec) + ec = asio::error::invalid_argument; + if (result != 0 && af == ASIO_OS_DEF(AF_INET6) && scope_id != 0) + { + using namespace std; // For strcat and sprintf. + char if_name[(IF_NAMESIZE > 21 ? IF_NAMESIZE : 21) + 1] = "%"; + const in6_addr_type* ipv6_address = static_cast(src); + bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe) + && ((ipv6_address->s6_addr[1] & 0xc0) == 0x80)); + bool is_multicast_link_local = ((ipv6_address->s6_addr[0] == 0xff) + && ((ipv6_address->s6_addr[1] & 0x0f) == 0x02)); + if ((!is_link_local && !is_multicast_link_local) + || if_indextoname(static_cast(scope_id), if_name + 1) == 0) + sprintf(if_name + 1, "%lu", scope_id); + strcat(dest, if_name); + } + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS_RUNTIME) + using namespace std; // For sscanf. + unsigned char* bytes = static_cast(dest); + if (af == ASIO_OS_DEF(AF_INET)) + { + unsigned int b0, b1, b2, b3; + if (sscanf_s(src, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) != 4) + { + ec = asio::error::invalid_argument; + return -1; + } + if (b0 > 255 || b1 > 255 || b2 > 255 || b3 > 255) + { + ec = asio::error::invalid_argument; + return -1; + } + bytes[0] = static_cast(b0); + bytes[1] = static_cast(b1); + bytes[2] = static_cast(b2); + bytes[3] = static_cast(b3); + ec = asio::error_code(); + return 1; + } + else if (af == ASIO_OS_DEF(AF_INET6)) + { + unsigned char* bytes = static_cast(dest); + std::memset(bytes, 0, 16); + unsigned char back_bytes[16] = { 0 }; + int num_front_bytes = 0, num_back_bytes = 0; + const char* p = src; + + enum { fword, fcolon, bword, scope, done } state = fword; + unsigned long current_word = 0; + while (state != done) + { + if (current_word > 0xFFFF) + { + ec = asio::error::invalid_argument; + return -1; + } + + switch (state) + { + case fword: + if (*p >= '0' && *p <= '9') + current_word = current_word * 16 + *p++ - '0'; + else if (*p >= 'a' && *p <= 'f') + current_word = current_word * 16 + *p++ - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + current_word = current_word * 16 + *p++ - 'A' + 10; + else + { + if (num_front_bytes == 16) + { + ec = asio::error::invalid_argument; + return -1; + } + + bytes[num_front_bytes++] = (current_word >> 8) & 0xFF; + bytes[num_front_bytes++] = current_word & 0xFF; + current_word = 0; + + if (*p == ':') + state = fcolon, ++p; + else if (*p == '%') + state = scope, ++p; + else if (*p == 0) + state = done; + else + { + ec = asio::error::invalid_argument; + return -1; + } + } + break; + + case fcolon: + if (*p == ':') + state = bword, ++p; + else + state = fword; + break; + + case bword: + if (*p >= '0' && *p <= '9') + current_word = current_word * 16 + *p++ - '0'; + else if (*p >= 'a' && *p <= 'f') + current_word = current_word * 16 + *p++ - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + current_word = current_word * 16 + *p++ - 'A' + 10; + else + { + if (num_front_bytes + num_back_bytes == 16) + { + ec = asio::error::invalid_argument; + return -1; + } + + back_bytes[num_back_bytes++] = (current_word >> 8) & 0xFF; + back_bytes[num_back_bytes++] = current_word & 0xFF; + current_word = 0; + + if (*p == ':') + state = bword, ++p; + else if (*p == '%') + state = scope, ++p; + else if (*p == 0) + state = done; + else + { + ec = asio::error::invalid_argument; + return -1; + } + } + break; + + case scope: + if (*p >= '0' && *p <= '9') + current_word = current_word * 10 + *p++ - '0'; + else if (*p == 0) + *scope_id = current_word, state = done; + else + { + ec = asio::error::invalid_argument; + return -1; + } + break; + + default: + break; + } + } + + for (int i = 0; i < num_back_bytes; ++i) + bytes[16 - num_back_bytes + i] = back_bytes[i]; + + ec = asio::error_code(); + return 1; + } + else + { + ec = asio::error::address_family_not_supported; + return -1; + } +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy and strcmp. + + if (af != ASIO_OS_DEF(AF_INET) && af != ASIO_OS_DEF(AF_INET6)) + { + ec = asio::error::address_family_not_supported; + return -1; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + int address_length = sizeof(sockaddr_storage_type); +#if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) + int num_wide_chars = static_cast(strlen(src)) + 1; + LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); + ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); + int result = error_wrapper(::WSAStringToAddressW( + wide_buffer, af, 0, &address.base, &address_length), ec); +#else + int result = error_wrapper(::WSAStringToAddressA( + const_cast(src), af, 0, &address.base, &address_length), ec); +#endif + + if (af == ASIO_OS_DEF(AF_INET)) + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); + ec = asio::error_code(); + } + else if (strcmp(src, "255.255.255.255") == 0) + { + static_cast(dest)->s_addr = INADDR_NONE; + ec = asio::error_code(); + } + } + else // AF_INET6 + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); + if (scope_id) + *scope_id = address.v6.sin6_scope_id; + ec = asio::error_code(); + } + } + + // Windows may not set an error code on failure. + if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + if (result != socket_error_retval) + ec = asio::error_code(); + + return result == socket_error_retval ? -1 : 1; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For strchr, memcpy and atoi. + + // On some platforms, inet_pton fails if an address string contains a scope + // id. Detect and remove the scope id before passing the string to inet_pton. + const bool is_v6 = (af == ASIO_OS_DEF(AF_INET6)); + const char* if_name = is_v6 ? strchr(src, '%') : 0; + char src_buf[max_addr_v6_str_len + 1]; + const char* src_ptr = src; + if (if_name != 0) + { + if (if_name - src > max_addr_v6_str_len) + { + ec = asio::error::invalid_argument; + return 0; + } + memcpy(src_buf, src, if_name - src); + src_buf[if_name - src] = 0; + src_ptr = src_buf; + } + + int result = error_wrapper(::inet_pton(af, src_ptr, dest), ec); + if (result <= 0 && !ec) + ec = asio::error::invalid_argument; + if (result > 0 && is_v6 && scope_id) + { + using namespace std; // For strchr and atoi. + *scope_id = 0; + if (if_name != 0) + { + in6_addr_type* ipv6_address = static_cast(dest); + bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe) + && ((ipv6_address->s6_addr[1] & 0xc0) == 0x80)); + bool is_multicast_link_local = ((ipv6_address->s6_addr[0] == 0xff) + && ((ipv6_address->s6_addr[1] & 0x0f) == 0x02)); + if (is_link_local || is_multicast_link_local) + *scope_id = if_nametoindex(if_name + 1); + if (*scope_id == 0) + *scope_id = atoi(if_name + 1); + } + } + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +int gethostname(char* name, int namelen, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS_RUNTIME) + try + { + using namespace Windows::Foundation::Collections; + using namespace Windows::Networking; + using namespace Windows::Networking::Connectivity; + IVectorView^ hostnames = NetworkInformation::GetHostNames(); + for (unsigned i = 0; i < hostnames->Size; ++i) + { + HostName^ hostname = hostnames->GetAt(i); + if (hostname->Type == HostNameType::DomainName) + { + std::wstring_convert> converter; + std::string raw_name = converter.to_bytes(hostname->RawName->Data()); + if (namelen > 0 && raw_name.size() < static_cast(namelen)) + { + strcpy_s(name, namelen, raw_name.c_str()); + return 0; + } + } + } + return -1; + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return -1; + } +#else // defined(ASIO_WINDOWS_RUNTIME) + int result = error_wrapper(::gethostname(name, namelen), ec); +# if defined(ASIO_WINDOWS) + if (result == 0) + ec = asio::error_code(); +# endif // defined(ASIO_WINDOWS) + return result; +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if !defined(ASIO_HAS_GETADDRINFO) + +// The following functions are only needed for emulation of getaddrinfo and +// getnameinfo. + +inline asio::error_code translate_netdb_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case HOST_NOT_FOUND: + return asio::error::host_not_found; + case TRY_AGAIN: + return asio::error::host_not_found_try_again; + case NO_RECOVERY: + return asio::error::no_recovery; + case NO_DATA: + return asio::error::no_data; + default: + ASIO_ASSERT(false); + return asio::error::invalid_argument; + } +} + +inline hostent* gethostbyaddr(const char* addr, int length, int af, + hostent* result, char* buffer, int buflength, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec); + if (!retval) + return 0; + ec = asio::error_code(); + *result = *retval; + return retval; +#elif defined(__sun) || defined(__QNX__) + int error = 0; + hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result, + buffer, buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyaddr( + addr, length, af, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, + buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline hostent* gethostbyname(const char* name, int af, struct hostent* result, + char* buffer, int buflength, int ai_flags, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + (void)(ai_flags); + if (af != ASIO_OS_DEF(AF_INET)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = error_wrapper(::gethostbyname(name), ec); + if (!retval) + return 0; + ec = asio::error_code(); + *result = *retval; + return result; +#elif defined(__sun) || defined(__QNX__) + (void)(ai_flags); + if (af != ASIO_OS_DEF(AF_INET)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + int error = 0; + hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, + buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyname( + name, af, ai_flags, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + (void)(ai_flags); + if (af != ASIO_OS_DEF(AF_INET)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyname_r(name, result, + buffer, buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline void freehostent(hostent* h) +{ +#if defined(__MACH__) && defined(__APPLE__) + if (h) + ::freehostent(h); +#else + (void)(h); +#endif +} + +// Emulation of getaddrinfo based on implementation in: +// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. + +struct gai_search +{ + const char* host; + int family; +}; + +inline int gai_nsearch(const char* host, + const addrinfo_type* hints, gai_search (&search)[2]) +{ + int search_count = 0; + if (host == 0 || host[0] == '\0') + { + if (hints->ai_flags & AI_PASSIVE) + { + // No host and AI_PASSIVE implies wildcard bind. + switch (hints->ai_family) + { + case ASIO_OS_DEF(AF_INET): + search[search_count].host = "0.0.0.0"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + case ASIO_OS_DEF(AF_INET6): + search[search_count].host = "0::0"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + break; + case ASIO_OS_DEF(AF_UNSPEC): + search[search_count].host = "0::0"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + search[search_count].host = "0.0.0.0"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + default: + break; + } + } + else + { + // No host and not AI_PASSIVE means connect to local host. + switch (hints->ai_family) + { + case ASIO_OS_DEF(AF_INET): + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + case ASIO_OS_DEF(AF_INET6): + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + break; + case ASIO_OS_DEF(AF_UNSPEC): + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + default: + break; + } + } + } + else + { + // Host is specified. + switch (hints->ai_family) + { + case ASIO_OS_DEF(AF_INET): + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + case ASIO_OS_DEF(AF_INET6): + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + break; + case ASIO_OS_DEF(AF_UNSPEC): + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + default: + break; + } + } + return search_count; +} + +template +inline T* gai_alloc(std::size_t size = sizeof(T)) +{ + using namespace std; + T* p = static_cast(::operator new(size, std::nothrow)); + if (p) + memset(p, 0, size); + return p; +} + +inline void gai_free(void* p) +{ + ::operator delete(p); +} + +inline void gai_strcpy(char* target, const char* source, std::size_t max_size) +{ + using namespace std; +#if defined(ASIO_HAS_SECURE_RTL) + strcpy_s(target, max_size, source); +#else // defined(ASIO_HAS_SECURE_RTL) + *target = 0; + if (max_size > 0) + strncat(target, source, max_size - 1); +#endif // defined(ASIO_HAS_SECURE_RTL) +} + +enum { gai_clone_flag = 1 << 30 }; + +inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, + const void* addr, int family) +{ + using namespace std; + + addrinfo_type* ai = gai_alloc(); + if (ai == 0) + return EAI_MEMORY; + + ai->ai_next = 0; + **next = ai; + *next = &ai->ai_next; + + ai->ai_canonname = 0; + ai->ai_socktype = hints->ai_socktype; + if (ai->ai_socktype == 0) + ai->ai_flags |= gai_clone_flag; + ai->ai_protocol = hints->ai_protocol; + ai->ai_family = family; + + switch (ai->ai_family) + { + case ASIO_OS_DEF(AF_INET): + { + sockaddr_in4_type* sinptr = gai_alloc(); + if (sinptr == 0) + return EAI_MEMORY; + sinptr->sin_family = ASIO_OS_DEF(AF_INET); + memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); + ai->ai_addr = reinterpret_cast(sinptr); + ai->ai_addrlen = sizeof(sockaddr_in4_type); + break; + } + case ASIO_OS_DEF(AF_INET6): + { + sockaddr_in6_type* sin6ptr = gai_alloc(); + if (sin6ptr == 0) + return EAI_MEMORY; + sin6ptr->sin6_family = ASIO_OS_DEF(AF_INET6); + memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); + ai->ai_addr = reinterpret_cast(sin6ptr); + ai->ai_addrlen = sizeof(sockaddr_in6_type); + break; + } + default: + break; + } + + return 0; +} + +inline addrinfo_type* gai_clone(addrinfo_type* ai) +{ + using namespace std; + + addrinfo_type* new_ai = gai_alloc(); + if (new_ai == 0) + return new_ai; + + new_ai->ai_next = ai->ai_next; + ai->ai_next = new_ai; + + new_ai->ai_flags = 0; + new_ai->ai_family = ai->ai_family; + new_ai->ai_socktype = ai->ai_socktype; + new_ai->ai_protocol = ai->ai_protocol; + new_ai->ai_canonname = 0; + new_ai->ai_addrlen = ai->ai_addrlen; + new_ai->ai_addr = gai_alloc(ai->ai_addrlen); + memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); + + return new_ai; +} + +inline int gai_port(addrinfo_type* aihead, int port, int socktype) +{ + int num_found = 0; + + for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) + { + if (ai->ai_flags & gai_clone_flag) + { + if (ai->ai_socktype != 0) + { + ai = gai_clone(ai); + if (ai == 0) + return -1; + // ai now points to newly cloned entry. + } + } + else if (ai->ai_socktype != socktype) + { + // Ignore if mismatch on socket type. + continue; + } + + ai->ai_socktype = socktype; + + switch (ai->ai_family) + { + case ASIO_OS_DEF(AF_INET): + { + sockaddr_in4_type* sinptr = + reinterpret_cast(ai->ai_addr); + sinptr->sin_port = port; + ++num_found; + break; + } + case ASIO_OS_DEF(AF_INET6): + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast(ai->ai_addr); + sin6ptr->sin6_port = port; + ++num_found; + break; + } + default: + break; + } + } + + return num_found; +} + +inline int gai_serv(addrinfo_type* aihead, + const addrinfo_type* hints, const char* serv) +{ + using namespace std; + + int num_found = 0; + + if ( +#if defined(AI_NUMERICSERV) + (hints->ai_flags & AI_NUMERICSERV) || +#endif + isdigit(static_cast(serv[0]))) + { + int port = htons(atoi(serv)); + if (hints->ai_socktype) + { + // Caller specifies socket type. + int rc = gai_port(aihead, port, hints->ai_socktype); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + else + { + // Caller does not specify socket type. + int rc = gai_port(aihead, port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + rc = gai_port(aihead, port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + else + { + // Try service name with TCP first, then UDP. + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) + { + servent* sptr = getservbyname(serv, "tcp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) + { + servent* sptr = getservbyname(serv, "udp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + } + + if (num_found == 0) + { + if (hints->ai_socktype == 0) + { + // All calls to getservbyname() failed. + return EAI_NONAME; + } + else + { + // Service not supported for socket type. + return EAI_SERVICE; + } + } + + return 0; +} + +inline int gai_echeck(const char* host, const char* service, + int flags, int family, int socktype, int protocol) +{ + (void)(flags); + (void)(protocol); + + // Host or service must be specified. + if (host == 0 || host[0] == '\0') + if (service == 0 || service[0] == '\0') + return EAI_NONAME; + + // Check combination of family and socket type. + switch (family) + { + case ASIO_OS_DEF(AF_UNSPEC): + break; + case ASIO_OS_DEF(AF_INET): + case ASIO_OS_DEF(AF_INET6): + if (service != 0 && service[0] != '\0') + if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return EAI_SOCKTYPE; + break; + default: + return EAI_FAMILY; + } + + return 0; +} + +inline void freeaddrinfo_emulation(addrinfo_type* aihead) +{ + addrinfo_type* ai = aihead; + while (ai) + { + gai_free(ai->ai_addr); + gai_free(ai->ai_canonname); + addrinfo_type* ainext = ai->ai_next; + gai_free(ai); + ai = ainext; + } +} + +inline int getaddrinfo_emulation(const char* host, const char* service, + const addrinfo_type* hintsp, addrinfo_type** result) +{ + // Set up linked list of addrinfo structures. + addrinfo_type* aihead = 0; + addrinfo_type** ainext = &aihead; + char* canon = 0; + + // Supply default hints if not specified by caller. + addrinfo_type hints = addrinfo_type(); + hints.ai_family = ASIO_OS_DEF(AF_UNSPEC); + if (hintsp) + hints = *hintsp; + + // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED + // and AI_ALL flags. +#if defined(AI_V4MAPPED) + if (hints.ai_family != ASIO_OS_DEF(AF_INET6)) + hints.ai_flags &= ~AI_V4MAPPED; +#endif +#if defined(AI_ALL) + if (hints.ai_family != ASIO_OS_DEF(AF_INET6)) + hints.ai_flags &= ~AI_ALL; +#endif + + // Basic error checking. + int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, + hints.ai_socktype, hints.ai_protocol); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + + gai_search search[2]; + int search_count = gai_nsearch(host, &hints, search); + for (gai_search* sptr = search; sptr < search + search_count; ++sptr) + { + // Check for IPv4 dotted decimal string. + in4_addr_type inaddr; + asio::error_code ec; + if (socket_ops::inet_pton(ASIO_OS_DEF(AF_INET), + sptr->host, &inaddr, 0, ec) == 1) + { + if (hints.ai_family != ASIO_OS_DEF(AF_UNSPEC) + && hints.ai_family != ASIO_OS_DEF(AF_INET)) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == ASIO_OS_DEF(AF_INET)) + { + rc = gai_aistruct(&ainext, &hints, &inaddr, ASIO_OS_DEF(AF_INET)); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Check for IPv6 hex string. + in6_addr_type in6addr; + if (socket_ops::inet_pton(ASIO_OS_DEF(AF_INET6), + sptr->host, &in6addr, 0, ec) == 1) + { + if (hints.ai_family != ASIO_OS_DEF(AF_UNSPEC) + && hints.ai_family != ASIO_OS_DEF(AF_INET6)) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == ASIO_OS_DEF(AF_INET6)) + { + rc = gai_aistruct(&ainext, &hints, &in6addr, + ASIO_OS_DEF(AF_INET6)); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Look up hostname. + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyname(sptr->host, + sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); + if (hptr == 0) + { + if (search_count == 2) + { + // Failure is OK if there are multiple searches. + continue; + } + freeaddrinfo_emulation(aihead); + gai_free(canon); + if (ec == asio::error::host_not_found) + return EAI_NONAME; + if (ec == asio::error::host_not_found_try_again) + return EAI_AGAIN; + if (ec == asio::error::no_recovery) + return EAI_FAIL; + if (ec == asio::error::no_data) + return EAI_NONAME; + return EAI_NONAME; + } + + // Check for address family mismatch if one was specified. + if (hints.ai_family != ASIO_OS_DEF(AF_UNSPEC) + && hints.ai_family != hptr->h_addrtype) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + + // Save canonical name first time. + if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] + && (hints.ai_flags & AI_CANONNAME) && canon == 0) + { + std::size_t canon_len = strlen(hptr->h_name) + 1; + canon = gai_alloc(canon_len); + if (canon == 0) + { + freeaddrinfo_emulation(aihead); + socket_ops::freehostent(hptr); + return EAI_MEMORY; + } + gai_strcpy(canon, hptr->h_name, canon_len); + } + + // Create an addrinfo structure for each returned address. + for (char** ap = hptr->h_addr_list; *ap; ++ap) + { + rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + } + + socket_ops::freehostent(hptr); + } + + // Check if we found anything. + if (aihead == 0) + { + gai_free(canon); + return EAI_NONAME; + } + + // Return canonical name in first entry. + if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) + { + if (canon) + { + aihead->ai_canonname = canon; + canon = 0; + } + else + { + std::size_t canonname_len = strlen(search[0].host) + 1; + aihead->ai_canonname = gai_alloc(canonname_len); + if (aihead->ai_canonname == 0) + { + freeaddrinfo_emulation(aihead); + return EAI_MEMORY; + } + gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); + } + } + gai_free(canon); + + // Process the service name. + if (service != 0 && service[0] != '\0') + { + rc = gai_serv(aihead, &hints, service); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + } + + // Return result to caller. + *result = aihead; + return 0; +} + +inline asio::error_code getnameinfo_emulation( + const socket_addr_type* sa, std::size_t salen, char* host, + std::size_t hostlen, char* serv, std::size_t servlen, int flags, + asio::error_code& ec) +{ + using namespace std; + + const char* addr; + size_t addr_len; + unsigned short port; + switch (sa->sa_family) + { + case ASIO_OS_DEF(AF_INET): + if (salen != sizeof(sockaddr_in4_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin_addr); + addr_len = sizeof(in4_addr_type); + port = reinterpret_cast(sa)->sin_port; + break; + case ASIO_OS_DEF(AF_INET6): + if (salen != sizeof(sockaddr_in6_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin6_addr); + addr_len = sizeof(in6_addr_type); + port = reinterpret_cast(sa)->sin6_port; + break; + default: + return ec = asio::error::address_family_not_supported; + } + + if (host && hostlen > 0) + { + if (flags & NI_NUMERICHOST) + { + if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + else + { + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyaddr(addr, + static_cast(addr_len), sa->sa_family, + &hent, hbuf, sizeof(hbuf), ec); + if (hptr && hptr->h_name && hptr->h_name[0] != '\0') + { + if (flags & NI_NOFQDN) + { + char* dot = strchr(hptr->h_name, '.'); + if (dot) + { + *dot = 0; + } + } + gai_strcpy(host, hptr->h_name, hostlen); + socket_ops::freehostent(hptr); + } + else + { + socket_ops::freehostent(hptr); + if (flags & NI_NAMEREQD) + { + return ec = asio::error::host_not_found; + } + if (socket_ops::inet_ntop(sa->sa_family, + addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + } + } + + if (serv && servlen > 0) + { + if (flags & NI_NUMERICSERV) + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if defined(ASIO_HAS_SECURE_RTL) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else // defined(ASIO_HAS_SECURE_RTL) + sprintf(serv, "%u", ntohs(port)); +#endif // defined(ASIO_HAS_SECURE_RTL) + } + else + { +#if defined(ASIO_HAS_PTHREADS) + static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + ::pthread_mutex_lock(&mutex); +#endif // defined(ASIO_HAS_PTHREADS) + servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); + if (sptr && sptr->s_name && sptr->s_name[0] != '\0') + { + gai_strcpy(serv, sptr->s_name, servlen); + } + else + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if defined(ASIO_HAS_SECURE_RTL) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else // defined(ASIO_HAS_SECURE_RTL) + sprintf(serv, "%u", ntohs(port)); +#endif // defined(ASIO_HAS_SECURE_RTL) + } +#if defined(ASIO_HAS_PTHREADS) + ::pthread_mutex_unlock(&mutex); +#endif // defined(ASIO_HAS_PTHREADS) + } + } + + ec = asio::error_code(); + return ec; +} + +#endif // !defined(ASIO_HAS_GETADDRINFO) + +inline asio::error_code translate_addrinfo_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case EAI_AGAIN: + return asio::error::host_not_found_try_again; + case EAI_BADFLAGS: + return asio::error::invalid_argument; + case EAI_FAIL: + return asio::error::no_recovery; + case EAI_FAMILY: + return asio::error::address_family_not_supported; + case EAI_MEMORY: + return asio::error::no_memory; + case EAI_NONAME: +#if defined(EAI_ADDRFAMILY) + case EAI_ADDRFAMILY: +#endif +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif + return asio::error::host_not_found; + case EAI_SERVICE: + return asio::error::service_not_found; + case EAI_SOCKTYPE: + return asio::error::socket_type_not_supported; + default: // Possibly the non-portable EAI_SYSTEM. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + return asio::error_code( + WSAGetLastError(), asio::error::get_system_category()); +#else + return asio::error_code( + errno, asio::error::get_system_category()); +#endif + } +} + +asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec) +{ + host = (host && *host) ? host : 0; + service = (service && *service) ? service : 0; + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(ASIO_HAS_GETADDRINFO) + // Building for Windows XP, Windows Server 2003, or later. + int error = ::getaddrinfo(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gai_t)(const char*, + const char*, const addrinfo_type*, addrinfo_type**); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) + { + int error = gai(host, service, &hints, result); + return ec = translate_addrinfo_error(error); + } + } + int error = getaddrinfo_emulation(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +# endif +#elif !defined(ASIO_HAS_GETADDRINFO) + int error = getaddrinfo_emulation(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +#else + int error = ::getaddrinfo(host, service, &hints, result); +#if defined(__MACH__) && defined(__APPLE__) + using namespace std; // For isdigit and atoi. + if (error == 0 && service && isdigit(static_cast(service[0]))) + { + u_short_type port = host_to_network_short(atoi(service)); + for (addrinfo_type* ai = *result; ai; ai = ai->ai_next) + { + switch (ai->ai_family) + { + case ASIO_OS_DEF(AF_INET): + { + sockaddr_in4_type* sinptr = + reinterpret_cast(ai->ai_addr); + if (sinptr->sin_port == 0) + sinptr->sin_port = port; + break; + } + case ASIO_OS_DEF(AF_INET6): + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast(ai->ai_addr); + if (sin6ptr->sin6_port == 0) + sin6ptr->sin6_port = port; + break; + } + default: + break; + } + } + } +#endif + return ec = translate_addrinfo_error(error); +#endif +} + +asio::error_code background_getaddrinfo( + const weak_cancel_token_type& cancel_token, const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec) +{ + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + socket_ops::getaddrinfo(host, service, hints, result, ec); + return ec; +} + +void freeaddrinfo(addrinfo_type* ai) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(ASIO_HAS_GETADDRINFO) + // Building for Windows XP, Windows Server 2003, or later. + ::freeaddrinfo(ai); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *fai_t)(addrinfo_type*); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) + { + fai(ai); + return; + } + } + freeaddrinfo_emulation(ai); +# endif +#elif !defined(ASIO_HAS_GETADDRINFO) + freeaddrinfo_emulation(ai); +#else + ::freeaddrinfo(ai); +#endif +} + +asio::error_code getnameinfo(const socket_addr_type* addr, + std::size_t addrlen, char* host, std::size_t hostlen, + char* serv, std::size_t servlen, int flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(ASIO_HAS_GETADDRINFO) + // Building for Windows XP, Windows Server 2003, or later. + clear_last_error(); + int error = ::getnameinfo(addr, static_cast(addrlen), + host, static_cast(hostlen), + serv, static_cast(servlen), flags); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gni_t)(const socket_addr_type*, + int, char*, DWORD, char*, DWORD, int); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) + { + clear_last_error(); + int error = gni(addr, static_cast(addrlen), + host, static_cast(hostlen), + serv, static_cast(servlen), flags); + return ec = translate_addrinfo_error(error); + } + } + clear_last_error(); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +# endif +#elif !defined(ASIO_HAS_GETADDRINFO) + using namespace std; // For memcpy. + sockaddr_storage_type tmp_addr; + memcpy(&tmp_addr, addr, addrlen); + addr = reinterpret_cast(&tmp_addr); + clear_last_error(); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +#else + clear_last_error(); + int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + return ec = translate_addrinfo_error(error); +#endif +} + +asio::error_code sync_getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec) +{ + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; + socket_ops::getnameinfo(addr, addrlen, host, + hostlen, serv, servlen, flags, ec); + if (ec) + { + socket_ops::getnameinfo(addr, addrlen, host, hostlen, + serv, servlen, flags | NI_NUMERICSERV, ec); + } + + return ec; +} + +asio::error_code background_getnameinfo( + const weak_cancel_token_type& cancel_token, + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec) +{ + if (cancel_token.expired()) + { + ec = asio::error::operation_aborted; + } + else + { + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; + socket_ops::getnameinfo(addr, addrlen, host, + hostlen, serv, servlen, flags, ec); + if (ec) + { + socket_ops::getnameinfo(addr, addrlen, host, hostlen, + serv, servlen, flags | NI_NUMERICSERV, ec); + } + } + + return ec; +} + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +u_long_type network_to_host_long(u_long_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + unsigned char* value_p = reinterpret_cast(&value); + u_long_type result = (static_cast(value_p[0]) << 24) + | (static_cast(value_p[1]) << 16) + | (static_cast(value_p[2]) << 8) + | static_cast(value_p[3]); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return ntohl(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +u_long_type host_to_network_long(u_long_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + u_long_type result; + unsigned char* result_p = reinterpret_cast(&result); + result_p[0] = static_cast((value >> 24) & 0xFF); + result_p[1] = static_cast((value >> 16) & 0xFF); + result_p[2] = static_cast((value >> 8) & 0xFF); + result_p[3] = static_cast(value & 0xFF); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return htonl(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +u_short_type network_to_host_short(u_short_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + unsigned char* value_p = reinterpret_cast(&value); + u_short_type result = (static_cast(value_p[0]) << 8) + | static_cast(value_p[1]); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return ntohs(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +u_short_type host_to_network_short(u_short_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + u_short_type result; + unsigned char* result_p = reinterpret_cast(&result); + result_p[0] = static_cast((value >> 8) & 0xFF); + result_p[1] = static_cast(value & 0xFF); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return htons(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPS_IPP diff --git a/subprojects/asio/include/asio/detail/impl/socket_select_interrupter.ipp b/subprojects/asio/include/asio/detail/impl/socket_select_interrupter.ipp new file mode 100644 index 000000000..052f2a765 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/socket_select_interrupter.ipp @@ -0,0 +1,176 @@ +// +// detail/impl/socket_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + +#include +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_select_interrupter.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +socket_select_interrupter::socket_select_interrupter() +{ + open_descriptors(); +} + +void socket_select_interrupter::open_descriptors() +{ + asio::error_code ec; + socket_holder acceptor(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (acceptor.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + int opt = 1; + socket_ops::state_type acceptor_state = 0; + socket_ops::setsockopt(acceptor.get(), acceptor_state, + SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); + + using namespace std; // For memset. + sockaddr_in4_type addr; + std::size_t addr_len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK); + addr.sin_port = 0; + if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, + &addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + // Some broken firewalls on Windows will intermittently cause getsockname to + // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We + // explicitly specify the target address here to work around this problem. + if (addr.sin_addr.s_addr == socket_ops::host_to_network_long(INADDR_ANY)) + addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK); + + if (socket_ops::listen(acceptor.get(), + SOMAXCONN, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + socket_holder client(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (client.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); + if (server.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + ioctl_arg_type non_blocking = 1; + socket_ops::state_type client_state = 0; + if (socket_ops::ioctl(client.get(), client_state, + FIONBIO, &non_blocking, ec)) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + opt = 1; + socket_ops::setsockopt(client.get(), client_state, + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + non_blocking = 1; + socket_ops::state_type server_state = 0; + if (socket_ops::ioctl(server.get(), server_state, + FIONBIO, &non_blocking, ec)) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + opt = 1; + socket_ops::setsockopt(server.get(), server_state, + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + read_descriptor_ = server.release(); + write_descriptor_ = client.release(); +} + +socket_select_interrupter::~socket_select_interrupter() +{ + close_descriptors(); +} + +void socket_select_interrupter::close_descriptors() +{ + asio::error_code ec; + socket_ops::state_type state = socket_ops::internal_non_blocking; + if (read_descriptor_ != invalid_socket) + socket_ops::close(read_descriptor_, state, true, ec); + if (write_descriptor_ != invalid_socket) + socket_ops::close(write_descriptor_, state, true, ec); +} + +void socket_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = invalid_socket; + read_descriptor_ = invalid_socket; + + open_descriptors(); +} + +void socket_select_interrupter::interrupt() +{ + char byte = 0; + socket_ops::buf b; + socket_ops::init_buf(b, &byte, 1); + asio::error_code ec; + socket_ops::send(write_descriptor_, &b, 1, 0, ec); +} + +bool socket_select_interrupter::reset() +{ + char data[1024]; + socket_ops::buf b; + socket_ops::init_buf(b, data, sizeof(data)); + asio::error_code ec; + int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + return was_interrupted; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP diff --git a/subprojects/asio/include/asio/detail/impl/strand_executor_service.hpp b/subprojects/asio/include/asio/detail/impl/strand_executor_service.hpp new file mode 100644 index 000000000..0e18ca05e --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/strand_executor_service.hpp @@ -0,0 +1,179 @@ +// +// detail/impl/strand_executor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP +#define ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/call_stack.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/executor_work_guard.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class strand_executor_service::invoker +{ +public: + invoker(const implementation_type& impl, Executor& ex) + : impl_(impl), + work_(ex) + { + } + + invoker(const invoker& other) + : impl_(other.impl_), + work_(other.work_) + { + } + +#if defined(ASIO_HAS_MOVE) + invoker(invoker&& other) + : impl_(ASIO_MOVE_CAST(implementation_type)(other.impl_)), + work_(ASIO_MOVE_CAST(executor_work_guard)(other.work_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + struct on_invoker_exit + { + invoker* this_; + + ~on_invoker_exit() + { + this_->impl_->mutex_->lock(); + this_->impl_->ready_queue_.push(this_->impl_->waiting_queue_); + bool more_handlers = this_->impl_->locked_ = + !this_->impl_->ready_queue_.empty(); + this_->impl_->mutex_->unlock(); + + if (more_handlers) + { + Executor ex(this_->work_.get_executor()); + recycling_allocator allocator; + ex.post(ASIO_MOVE_CAST(invoker)(*this_), allocator); + } + } + }; + + void operator()() + { + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl_.get()); + + // Ensure the next handler, if any, is scheduled on block exit. + on_invoker_exit on_exit = { this }; + (void)on_exit; + + // Run all ready handlers. No lock is required since the ready queue is + // accessed only within the strand. + asio::error_code ec; + while (scheduler_operation* o = impl_->ready_queue_.front()) + { + impl_->ready_queue_.pop(); + o->complete(impl_.get(), ec, 0); + } + } + +private: + implementation_type impl_; + executor_work_guard work_; +}; + +template +void strand_executor_service::dispatch(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a) +{ + typedef typename decay::type function_type; + + // If we are already in the strand then the function can run immediately. + if (call_stack::contains(impl.get())) + { + // Make a local, non-const copy of the function. + function_type tmp(ASIO_MOVE_CAST(Function)(function)); + + fenced_block b(fenced_block::full); + asio_handler_invoke_helpers::invoke(tmp, tmp); + return; + } + + // Allocate and construct an operation to wrap the function. + typedef executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a); + + ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, + "strand_executor", impl.get(), 0, "dispatch")); + + // Add the function to the strand and schedule the strand if required. + bool first = enqueue(impl, p.p); + p.v = p.p = 0; + if (first) + ex.dispatch(invoker(impl, ex), a); +} + +// Request invocation of the given function and return immediately. +template +void strand_executor_service::post(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a) +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a); + + ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, + "strand_executor", impl.get(), 0, "post")); + + // Add the function to the strand and schedule the strand if required. + bool first = enqueue(impl, p.p); + p.v = p.p = 0; + if (first) + ex.post(invoker(impl, ex), a); +} + +// Request invocation of the given function and return immediately. +template +void strand_executor_service::defer(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a) +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a); + + ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, + "strand_executor", impl.get(), 0, "defer")); + + // Add the function to the strand and schedule the strand if required. + bool first = enqueue(impl, p.p); + p.v = p.p = 0; + if (first) + ex.defer(invoker(impl, ex), a); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/impl/strand_executor_service.ipp b/subprojects/asio/include/asio/detail/impl/strand_executor_service.ipp new file mode 100644 index 000000000..365652ece --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/strand_executor_service.ipp @@ -0,0 +1,134 @@ +// +// detail/impl/strand_executor_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_IPP +#define ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/strand_executor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +strand_executor_service::strand_executor_service(execution_context& ctx) + : execution_context_service_base(ctx), + mutex_(), + salt_(0), + impl_list_(0) +{ +} + +void strand_executor_service::shutdown() +{ + op_queue ops; + + asio::detail::mutex::scoped_lock lock(mutex_); + + strand_impl* impl = impl_list_; + while (impl) + { + impl->mutex_->lock(); + impl->shutdown_ = true; + ops.push(impl->waiting_queue_); + ops.push(impl->ready_queue_); + impl->mutex_->unlock(); + impl = impl->next_; + } +} + +strand_executor_service::implementation_type +strand_executor_service::create_implementation() +{ + implementation_type new_impl(new strand_impl); + new_impl->locked_ = false; + new_impl->shutdown_ = false; + + asio::detail::mutex::scoped_lock lock(mutex_); + + // Select a mutex from the pool of shared mutexes. + std::size_t salt = salt_++; + std::size_t mutex_index = reinterpret_cast(new_impl.get()); + mutex_index += (reinterpret_cast(new_impl.get()) >> 3); + mutex_index ^= salt + 0x9e3779b9 + (mutex_index << 6) + (mutex_index >> 2); + mutex_index = mutex_index % num_mutexes; + if (!mutexes_[mutex_index].get()) + mutexes_[mutex_index].reset(new mutex); + new_impl->mutex_ = mutexes_[mutex_index].get(); + + // Insert implementation into linked list of all implementations. + new_impl->next_ = impl_list_; + new_impl->prev_ = 0; + if (impl_list_) + impl_list_->prev_ = new_impl.get(); + impl_list_ = new_impl.get(); + new_impl->service_ = this; + + return new_impl; +} + +strand_executor_service::strand_impl::~strand_impl() +{ + asio::detail::mutex::scoped_lock lock(service_->mutex_); + + // Remove implementation from linked list of all implementations. + if (service_->impl_list_ == this) + service_->impl_list_ = next_; + if (prev_) + prev_->next_ = next_; + if (next_) + next_->prev_= prev_; +} + +bool strand_executor_service::enqueue(const implementation_type& impl, + scheduler_operation* op) +{ + impl->mutex_->lock(); + if (impl->shutdown_) + { + impl->mutex_->unlock(); + op->destroy(); + return false; + } + else if (impl->locked_) + { + // Some other function already holds the strand lock. Enqueue for later. + impl->waiting_queue_.push(op); + impl->mutex_->unlock(); + return false; + } + else + { + // The function is acquiring the strand lock and so is responsible for + // scheduling the strand. + impl->locked_ = true; + impl->mutex_->unlock(); + impl->ready_queue_.push(op); + return true; + } +} + +bool strand_executor_service::running_in_this_thread( + const implementation_type& impl) +{ + return !!call_stack::contains(impl.get()); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/strand_service.hpp b/subprojects/asio/include/asio/detail/impl/strand_service.hpp new file mode 100644 index 000000000..da5b71694 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/strand_service.hpp @@ -0,0 +1,118 @@ +// +// detail/impl/strand_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/call_stack.hpp" +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +inline strand_service::strand_impl::strand_impl() + : operation(&strand_service::do_complete), + locked_(false) +{ +} + +struct strand_service::on_dispatch_exit +{ + io_context_impl* io_context_; + strand_impl* impl_; + + ~on_dispatch_exit() + { + impl_->mutex_.lock(); + impl_->ready_queue_.push(impl_->waiting_queue_); + bool more_handlers = impl_->locked_ = !impl_->ready_queue_.empty(); + impl_->mutex_.unlock(); + + if (more_handlers) + io_context_->post_immediate_completion(impl_, false); + } +}; + +template +void strand_service::dispatch(strand_service::implementation_type& impl, + Handler& handler) +{ + // If we are already in the strand then the handler can run immediately. + if (call_stack::contains(impl)) + { + fenced_block b(fenced_block::full); + asio_handler_invoke_helpers::invoke(handler, handler); + return; + } + + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((this->context(), + *p.p, "strand", impl, 0, "dispatch")); + + bool dispatch_immediately = do_dispatch(impl, p.p); + operation* o = p.p; + p.v = p.p = 0; + + if (dispatch_immediately) + { + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_dispatch_exit on_exit = { &io_context_, impl }; + (void)on_exit; + + completion_handler::do_complete( + &io_context_, o, asio::error_code(), 0); + } +} + +// Request the io_context to invoke the given handler and return immediately. +template +void strand_service::post(strand_service::implementation_type& impl, + Handler& handler) +{ + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((this->context(), + *p.p, "strand", impl, 0, "post")); + + do_post(impl, p.p, is_continuation); + p.v = p.p = 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/impl/strand_service.ipp b/subprojects/asio/include/asio/detail/impl/strand_service.ipp new file mode 100644 index 000000000..cbaf25b35 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/strand_service.ipp @@ -0,0 +1,177 @@ +// +// detail/impl/strand_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP +#define ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/strand_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct strand_service::on_do_complete_exit +{ + io_context_impl* owner_; + strand_impl* impl_; + + ~on_do_complete_exit() + { + impl_->mutex_.lock(); + impl_->ready_queue_.push(impl_->waiting_queue_); + bool more_handlers = impl_->locked_ = !impl_->ready_queue_.empty(); + impl_->mutex_.unlock(); + + if (more_handlers) + owner_->post_immediate_completion(impl_, true); + } +}; + +strand_service::strand_service(asio::io_context& io_context) + : asio::detail::service_base(io_context), + io_context_(asio::use_service(io_context)), + mutex_(), + salt_(0) +{ +} + +void strand_service::shutdown() +{ + op_queue ops; + + asio::detail::mutex::scoped_lock lock(mutex_); + + for (std::size_t i = 0; i < num_implementations; ++i) + { + if (strand_impl* impl = implementations_[i].get()) + { + ops.push(impl->waiting_queue_); + ops.push(impl->ready_queue_); + } + } +} + +void strand_service::construct(strand_service::implementation_type& impl) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + std::size_t salt = salt_++; +#if defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + std::size_t index = salt; +#else // defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + std::size_t index = reinterpret_cast(&impl); + index += (reinterpret_cast(&impl) >> 3); + index ^= salt + 0x9e3779b9 + (index << 6) + (index >> 2); +#endif // defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + index = index % num_implementations; + + if (!implementations_[index].get()) + implementations_[index].reset(new strand_impl); + impl = implementations_[index].get(); +} + +bool strand_service::running_in_this_thread( + const implementation_type& impl) const +{ + return call_stack::contains(impl) != 0; +} + +bool strand_service::do_dispatch(implementation_type& impl, operation* op) +{ + // If we are running inside the io_context, and no other handler already + // holds the strand lock, then the handler can run immediately. + bool can_dispatch = io_context_.can_dispatch(); + impl->mutex_.lock(); + if (can_dispatch && !impl->locked_) + { + // Immediate invocation is allowed. + impl->locked_ = true; + impl->mutex_.unlock(); + return true; + } + + if (impl->locked_) + { + // Some other handler already holds the strand lock. Enqueue for later. + impl->waiting_queue_.push(op); + impl->mutex_.unlock(); + } + else + { + // The handler is acquiring the strand lock and so is responsible for + // scheduling the strand. + impl->locked_ = true; + impl->mutex_.unlock(); + impl->ready_queue_.push(op); + io_context_.post_immediate_completion(impl, false); + } + + return false; +} + +void strand_service::do_post(implementation_type& impl, + operation* op, bool is_continuation) +{ + impl->mutex_.lock(); + if (impl->locked_) + { + // Some other handler already holds the strand lock. Enqueue for later. + impl->waiting_queue_.push(op); + impl->mutex_.unlock(); + } + else + { + // The handler is acquiring the strand lock and so is responsible for + // scheduling the strand. + impl->locked_ = true; + impl->mutex_.unlock(); + impl->ready_queue_.push(op); + io_context_.post_immediate_completion(impl, is_continuation); + } +} + +void strand_service::do_complete(void* owner, operation* base, + const asio::error_code& ec, std::size_t /*bytes_transferred*/) +{ + if (owner) + { + strand_impl* impl = static_cast(base); + + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_do_complete_exit on_exit; + on_exit.owner_ = static_cast(owner); + on_exit.impl_ = impl; + + // Run all ready handlers. No lock is required since the ready queue is + // accessed only within the strand. + while (operation* o = impl->ready_queue_.front()) + { + impl->ready_queue_.pop(); + o->complete(owner, ec, 0); + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/throw_error.ipp b/subprojects/asio/include/asio/detail/impl/throw_error.ipp new file mode 100644 index 000000000..a540cd20d --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/throw_error.ipp @@ -0,0 +1,60 @@ +// +// detail/impl/throw_error.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_THROW_ERROR_IPP +#define ASIO_DETAIL_IMPL_THROW_ERROR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void do_throw_error(const asio::error_code& err) +{ + asio::system_error e(err); + asio::detail::throw_exception(e); +} + +void do_throw_error(const asio::error_code& err, const char* location) +{ + // boostify: non-boost code starts here +#if defined(ASIO_MSVC) && defined(ASIO_HAS_STD_SYSTEM_ERROR) + // Microsoft's implementation of std::system_error is non-conformant in that + // it ignores the error code's message when a "what" string is supplied. We'll + // work around this by explicitly formatting the "what" string. + std::string what_msg = location; + what_msg += ": "; + what_msg += err.message(); + asio::system_error e(err, what_msg); + asio::detail::throw_exception(e); +#else // defined(ASIO_MSVC) && defined(ASIO_HAS_STD_SYSTEM_ERROR) + // boostify: non-boost code ends here + asio::system_error e(err, location); + asio::detail::throw_exception(e); + // boostify: non-boost code starts here +#endif // defined(ASIO_MSVC) && defined(ASIO_HAS_STD_SYSTEM_ERROR) + // boostify: non-boost code ends here +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_THROW_ERROR_IPP diff --git a/subprojects/asio/include/asio/detail/impl/timer_queue_ptime.ipp b/subprojects/asio/include/asio/detail/impl/timer_queue_ptime.ipp new file mode 100644 index 000000000..742837f84 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/timer_queue_ptime.ipp @@ -0,0 +1,91 @@ +// +// detail/impl/timer_queue_ptime.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_PTIME_IPP +#define ASIO_DETAIL_IMPL_TIMER_QUEUE_PTIME_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) + +#include "asio/detail/timer_queue_ptime.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +timer_queue >::timer_queue() +{ +} + +timer_queue >::~timer_queue() +{ +} + +bool timer_queue >::enqueue_timer( + const time_type& time, per_timer_data& timer, wait_op* op) +{ + return impl_.enqueue_timer(time, timer, op); +} + +bool timer_queue >::empty() const +{ + return impl_.empty(); +} + +long timer_queue >::wait_duration_msec( + long max_duration) const +{ + return impl_.wait_duration_msec(max_duration); +} + +long timer_queue >::wait_duration_usec( + long max_duration) const +{ + return impl_.wait_duration_usec(max_duration); +} + +void timer_queue >::get_ready_timers( + op_queue& ops) +{ + impl_.get_ready_timers(ops); +} + +void timer_queue >::get_all_timers( + op_queue& ops) +{ + impl_.get_all_timers(ops); +} + +std::size_t timer_queue >::cancel_timer( + per_timer_data& timer, op_queue& ops, std::size_t max_cancelled) +{ + return impl_.cancel_timer(timer, ops, max_cancelled); +} + +void timer_queue >::move_timer( + per_timer_data& target, per_timer_data& source) +{ + impl_.move_timer(target, source); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_PTIME_IPP diff --git a/subprojects/asio/include/asio/detail/impl/timer_queue_set.ipp b/subprojects/asio/include/asio/detail/impl/timer_queue_set.ipp new file mode 100644 index 000000000..b51654883 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/timer_queue_set.ipp @@ -0,0 +1,101 @@ +// +// detail/impl/timer_queue_set.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP +#define ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/timer_queue_set.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +timer_queue_set::timer_queue_set() + : first_(0) +{ +} + +void timer_queue_set::insert(timer_queue_base* q) +{ + q->next_ = first_; + first_ = q; +} + +void timer_queue_set::erase(timer_queue_base* q) +{ + if (first_) + { + if (q == first_) + { + first_ = q->next_; + q->next_ = 0; + return; + } + + for (timer_queue_base* p = first_; p->next_; p = p->next_) + { + if (p->next_ == q) + { + p->next_ = q->next_; + q->next_ = 0; + return; + } + } + } +} + +bool timer_queue_set::all_empty() const +{ + for (timer_queue_base* p = first_; p; p = p->next_) + if (!p->empty()) + return false; + return true; +} + +long timer_queue_set::wait_duration_msec(long max_duration) const +{ + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_msec(min_duration); + return min_duration; +} + +long timer_queue_set::wait_duration_usec(long max_duration) const +{ + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_usec(min_duration); + return min_duration; +} + +void timer_queue_set::get_ready_timers(op_queue& ops) +{ + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_ready_timers(ops); +} + +void timer_queue_set::get_all_timers(op_queue& ops) +{ + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_all_timers(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_event.ipp b/subprojects/asio/include/asio/detail/impl/win_event.ipp new file mode 100644 index 000000000..6f7464991 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_event.ipp @@ -0,0 +1,76 @@ +// +// detail/win_event.ipp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_EVENT_IPP +#define ASIO_DETAIL_IMPL_WIN_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_event.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_event::win_event() + : state_(0) +{ +#if defined(ASIO_WINDOWS_APP) + events_[0] = ::CreateEventExW(0, 0, + CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); +#else // defined(ASIO_WINDOWS_APP) + events_[0] = ::CreateEventW(0, true, false, 0); +#endif // defined(ASIO_WINDOWS_APP) + if (!events_[0]) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); + } + +#if defined(ASIO_WINDOWS_APP) + events_[1] = ::CreateEventExW(0, 0, 0, EVENT_ALL_ACCESS); +#else // defined(ASIO_WINDOWS_APP) + events_[1] = ::CreateEventW(0, false, false, 0); +#endif // defined(ASIO_WINDOWS_APP) + if (!events_[1]) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(events_[0]); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); + } +} + +win_event::~win_event() +{ + ::CloseHandle(events_[0]); + ::CloseHandle(events_[1]); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_EVENT_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_iocp_handle_service.ipp b/subprojects/asio/include/asio/detail/impl/win_iocp_handle_service.ipp new file mode 100644 index 000000000..9cba2b0fc --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_iocp_handle_service.ipp @@ -0,0 +1,525 @@ +// +// detail/impl/win_iocp_handle_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/win_iocp_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_handle_service::overlapped_wrapper + : public OVERLAPPED +{ +public: + explicit overlapped_wrapper(asio::error_code& ec) + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + + // Create a non-signalled manual-reset event, for GetOverlappedResult. + hEvent = ::CreateEventW(0, TRUE, FALSE, 0); + if (hEvent) + { + // As documented in GetQueuedCompletionStatus, setting the low order + // bit of this event prevents our synchronous writes from being treated + // as completion port events. + DWORD_PTR tmp = reinterpret_cast(hEvent); + hEvent = reinterpret_cast(tmp | 1); + } + else + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + + ~overlapped_wrapper() + { + if (hEvent) + { + ::CloseHandle(hEvent); + } + } +}; + +win_iocp_handle_service::win_iocp_handle_service( + asio::io_context& io_context) + : service_base(io_context), + iocp_service_(asio::use_service(io_context)), + mutex_(), + impl_list_(0) +{ +} + +void win_iocp_handle_service::shutdown() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + implementation_type* impl = impl_list_; + while (impl) + { + close_for_destruction(*impl); + impl = impl->next_; + } +} + +void win_iocp_handle_service::construct( + win_iocp_handle_service::implementation_type& impl) +{ + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_handle_service::move_construct( + win_iocp_handle_service::implementation_type& impl, + win_iocp_handle_service::implementation_type& other_impl) +{ + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_handle_service::move_assign( + win_iocp_handle_service::implementation_type& impl, + win_iocp_handle_service& other_service, + win_iocp_handle_service::implementation_type& other_impl) +{ + close_for_destruction(impl); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(other_service.mutex_); + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } +} + +void win_iocp_handle_service::destroy( + win_iocp_handle_service::implementation_type& impl) +{ + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code win_iocp_handle_service::assign( + win_iocp_handle_service::implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (iocp_service_.register_handle(handle, ec)) + return ec; + + impl.handle_ = handle; + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_handle_service::close( + win_iocp_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle", + &impl, reinterpret_cast(impl.handle_), "close")); + + if (!::CloseHandle(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } + else + { + ec = asio::error_code(); + } + + return ec; +} + +asio::error_code win_iocp_handle_service::cancel( + win_iocp_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle", + &impl, reinterpret_cast(impl.handle_), "cancel")); + + if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; + if (!cancel_io_ex(impl.handle_, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + if (!::CancelIo(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } + + return ec; +} + +size_t win_iocp_handle_service::do_write( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::const_buffer& buffer, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes on a handle is a no-op. + if (buffer.size() == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Write the data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING) + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; +} + +void win_iocp_handle_service::start_write_op( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::const_buffer& buffer, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (buffer.size() == 0) + { + // A request to write 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } +} + +size_t win_iocp_handle_service::do_read( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::mutable_buffer& buffer, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream handle is a no-op. + if (buffer.size() == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Read some data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) + { + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return (last_error == ERROR_MORE_DATA) ? bytes_transferred : 0; + } + + ec = asio::error_code(); + return bytes_transferred; +} + +void win_iocp_handle_service::start_read_op( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::mutable_buffer& buffer, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (buffer.size() == 0) + { + // A request to read 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } +} + +void win_iocp_handle_service::update_cancellation_thread_id( + win_iocp_handle_service::implementation_type& impl) +{ + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +} + +void win_iocp_handle_service::close_for_destruction(implementation_type& impl) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle", + &impl, reinterpret_cast(impl.handle_), "close")); + + ::CloseHandle(impl.handle_); + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_iocp_io_context.hpp b/subprojects/asio/include/asio/detail/impl/win_iocp_io_context.hpp new file mode 100644 index 000000000..44887d7c2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_iocp_io_context.hpp @@ -0,0 +1,103 @@ +// +// detail/impl/win_iocp_io_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_HPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void win_iocp_io_context::add_timer_queue( + timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void win_iocp_io_context::remove_timer_queue( + timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void win_iocp_io_context::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + // If the service has been shut down we silently discard the timer. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + { + post_immediate_completion(op, false); + return; + } + + mutex::scoped_lock lock(dispatch_mutex_); + + bool earliest = queue.enqueue_timer(time, timer, op); + work_started(); + if (earliest) + update_timeout(); +} + +template +std::size_t win_iocp_io_context::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + // If the service has been shut down we silently ignore the cancellation. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + return 0; + + mutex::scoped_lock lock(dispatch_mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + post_deferred_completions(ops); + return n; +} + +template +void win_iocp_io_context::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from) +{ + asio::detail::mutex::scoped_lock lock(dispatch_mutex_); + op_queue ops; + queue.cancel_timer(to, ops); + queue.move_timer(to, from); + lock.unlock(); + post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/detail/impl/win_iocp_io_context.ipp b/subprojects/asio/include/asio/detail/impl/win_iocp_io_context.ipp new file mode 100644 index 000000000..c371b86b2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_iocp_io_context.ipp @@ -0,0 +1,554 @@ +// +// detail/impl/win_iocp_io_context.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_iocp_io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct win_iocp_io_context::work_finished_on_block_exit +{ + ~work_finished_on_block_exit() + { + io_context_->work_finished(); + } + + win_iocp_io_context* io_context_; +}; + +struct win_iocp_io_context::timer_thread_function +{ + void operator()() + { + while (::InterlockedExchangeAdd(&io_context_->shutdown_, 0) == 0) + { + if (::WaitForSingleObject(io_context_->waitable_timer_.handle, + INFINITE) == WAIT_OBJECT_0) + { + ::InterlockedExchange(&io_context_->dispatch_required_, 1); + ::PostQueuedCompletionStatus(io_context_->iocp_.handle, + 0, wake_for_dispatch, 0); + } + } + } + + win_iocp_io_context* io_context_; +}; + +win_iocp_io_context::win_iocp_io_context( + asio::execution_context& ctx, int concurrency_hint) + : execution_context_service_base(ctx), + iocp_(), + outstanding_work_(0), + stopped_(0), + stop_event_posted_(0), + shutdown_(0), + gqcs_timeout_(get_gqcs_timeout()), + dispatch_required_(0), + concurrency_hint_(concurrency_hint) +{ + ASIO_HANDLER_TRACKING_INIT; + + iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, + static_cast(concurrency_hint >= 0 ? concurrency_hint : DWORD(~0))); + if (!iocp_.handle) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "iocp"); + } +} + +void win_iocp_io_context::shutdown() +{ + ::InterlockedExchange(&shutdown_, 1); + + if (timer_thread_.get()) + { + LARGE_INTEGER timeout; + timeout.QuadPart = 1; + ::SetWaitableTimer(waitable_timer_.handle, &timeout, 1, 0, 0, FALSE); + } + + while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0) + { + op_queue ops; + timer_queues_.get_all_timers(ops); + ops.push(completed_ops_); + if (!ops.empty()) + { + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + ::InterlockedDecrement(&outstanding_work_); + op->destroy(); + } + } + else + { + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, gqcs_timeout_); + if (overlapped) + { + ::InterlockedDecrement(&outstanding_work_); + static_cast(overlapped)->destroy(); + } + } + } + + if (timer_thread_.get()) + timer_thread_->join(); +} + +asio::error_code win_iocp_io_context::register_handle( + HANDLE handle, asio::error_code& ec) +{ + if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + return ec; +} + +size_t win_iocp_io_context::run(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + size_t n = 0; + while (do_one(INFINITE, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +size_t win_iocp_io_context::run_one(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + return do_one(INFINITE, ec); +} + +size_t win_iocp_io_context::wait_one(long usec, asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + return do_one(usec < 0 ? INFINITE : ((usec - 1) / 1000 + 1), ec); +} + +size_t win_iocp_io_context::poll(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + size_t n = 0; + while (do_one(0, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +size_t win_iocp_io_context::poll_one(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + return do_one(0, ec); +} + +void win_iocp_io_context::stop() +{ + if (::InterlockedExchange(&stopped_, 1) == 0) + { + if (::InterlockedExchange(&stop_event_posted_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "pqcs"); + } + } + } +} + +void win_iocp_io_context::post_deferred_completion(win_iocp_operation* op) +{ + // Flag the operation as ready. + op->ready_ = 1; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +void win_iocp_io_context::post_deferred_completions( + op_queue& ops) +{ + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + + // Flag the operation as ready. + op->ready_ = 1; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + completed_ops_.push(ops); + ::InterlockedExchange(&dispatch_required_, 1); + } + } +} + +void win_iocp_io_context::abandon_operations( + op_queue& ops) +{ + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + ::InterlockedDecrement(&outstanding_work_); + op->destroy(); + } +} + +void win_iocp_io_context::on_pending(win_iocp_operation* op) +{ + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } + } +} + +void win_iocp_io_context::on_completion(win_iocp_operation* op, + DWORD last_error, DWORD bytes_transferred) +{ + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = reinterpret_cast( + &asio::error::get_system_category()); + op->Offset = last_error; + op->OffsetHigh = bytes_transferred; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +void win_iocp_io_context::on_completion(win_iocp_operation* op, + const asio::error_code& ec, DWORD bytes_transferred) +{ + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = reinterpret_cast(&ec.category()); + op->Offset = ec.value(); + op->OffsetHigh = bytes_transferred; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +size_t win_iocp_io_context::do_one(DWORD msec, asio::error_code& ec) +{ + for (;;) + { + // Try to acquire responsibility for dispatching timers and completed ops. + if (::InterlockedCompareExchange(&dispatch_required_, 0, 1) == 1) + { + mutex::scoped_lock lock(dispatch_mutex_); + + // Dispatch pending timers and operations. + op_queue ops; + ops.push(completed_ops_); + timer_queues_.get_ready_timers(ops); + post_deferred_completions(ops); + update_timeout(); + } + + // Get the next operation from the queue. + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, + &bytes_transferred, &completion_key, &overlapped, + msec < gqcs_timeout_ ? msec : gqcs_timeout_); + DWORD last_error = ::GetLastError(); + + if (overlapped) + { + win_iocp_operation* op = static_cast(overlapped); + asio::error_code result_ec(last_error, + asio::error::get_system_category()); + + // We may have been passed the last_error and bytes_transferred in the + // OVERLAPPED structure itself. + if (completion_key == overlapped_contains_result) + { + result_ec = asio::error_code(static_cast(op->Offset), + *reinterpret_cast(op->Internal)); + bytes_transferred = op->OffsetHigh; + } + + // Otherwise ensure any result has been saved into the OVERLAPPED + // structure. + else + { + op->Internal = reinterpret_cast(&result_ec.category()); + op->Offset = result_ec.value(); + op->OffsetHigh = bytes_transferred; + } + + // Dispatch the operation only if ready. The operation may not be ready + // if the initiating function (e.g. a call to WSARecv) has not yet + // returned. This is because the initiating function still wants access + // to the operation's OVERLAPPED structure. + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Ensure the count of outstanding work is decremented on block exit. + work_finished_on_block_exit on_exit = { this }; + (void)on_exit; + + op->complete(this, result_ec, bytes_transferred); + ec = asio::error_code(); + return 1; + } + } + else if (!ok) + { + if (last_error != WAIT_TIMEOUT) + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + // If we're waiting indefinitely we need to keep going until we get a + // real handler. + if (msec == INFINITE) + continue; + + ec = asio::error_code(); + return 0; + } + else if (completion_key == wake_for_dispatch) + { + // We have been woken up to try to acquire responsibility for dispatching + // timers and completed operations. + } + else + { + // Indicate that there is no longer an in-flight stop event. + ::InterlockedExchange(&stop_event_posted_, 0); + + // The stopped_ flag is always checked to ensure that any leftover + // stop events from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&stopped_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + if (::InterlockedExchange(&stop_event_posted_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + } + + ec = asio::error_code(); + return 0; + } + } + } +} + +DWORD win_iocp_io_context::get_gqcs_timeout() +{ + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = 6ul; + + const uint64_t condition_mask = ::VerSetConditionMask( + 0, VER_MAJORVERSION, VER_GREATER_EQUAL); + + if (!!::VerifyVersionInfo(&osvi, VER_MAJORVERSION, condition_mask)) + return INFINITE; + + return default_gqcs_timeout; +} + +void win_iocp_io_context::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(dispatch_mutex_); + + timer_queues_.insert(&queue); + + if (!waitable_timer_.handle) + { + waitable_timer_.handle = ::CreateWaitableTimer(0, FALSE, 0); + if (waitable_timer_.handle == 0) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "timer"); + } + + LARGE_INTEGER timeout; + timeout.QuadPart = -max_timeout_usec; + timeout.QuadPart *= 10; + ::SetWaitableTimer(waitable_timer_.handle, + &timeout, max_timeout_msec, 0, 0, FALSE); + } + + if (!timer_thread_.get()) + { + timer_thread_function thread_function = { this }; + timer_thread_.reset(new thread(thread_function, 65536)); + } +} + +void win_iocp_io_context::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(dispatch_mutex_); + + timer_queues_.erase(&queue); +} + +void win_iocp_io_context::update_timeout() +{ + if (timer_thread_.get()) + { + // There's no point updating the waitable timer if the new timeout period + // exceeds the maximum timeout. In that case, we might as well wait for the + // existing period of the timer to expire. + long timeout_usec = timer_queues_.wait_duration_usec(max_timeout_usec); + if (timeout_usec < max_timeout_usec) + { + LARGE_INTEGER timeout; + timeout.QuadPart = -timeout_usec; + timeout.QuadPart *= 10; + ::SetWaitableTimer(waitable_timer_.handle, + &timeout, max_timeout_msec, 0, 0, FALSE); + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_iocp_serial_port_service.ipp b/subprojects/asio/include/asio/detail/impl/win_iocp_serial_port_service.ipp new file mode 100644 index 000000000..4a9b8cd9f --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_iocp_serial_port_service.ipp @@ -0,0 +1,181 @@ +// +// detail/impl/win_iocp_serial_port_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#include +#include "asio/detail/win_iocp_serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_iocp_serial_port_service::win_iocp_serial_port_service( + asio::io_context& io_context) + : service_base(io_context), + handle_service_(io_context) +{ +} + +void win_iocp_serial_port_service::shutdown() +{ +} + +asio::error_code win_iocp_serial_port_service::open( + win_iocp_serial_port_service::implementation_type& impl, + const std::string& device, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + // For convenience, add a leading \\.\ sequence if not already present. + std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device; + + // Open a handle to the serial port. + ::HANDLE handle = ::CreateFileA(name.c_str(), + GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Determine the initial serial port parameters. + using namespace std; // For memset. + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set some default serial port parameters. This implementation does not + // support changing these, so they might as well be in a known state. + dcb.fBinary = TRUE; // Win32 only supports binary mode. + dcb.fDsrSensitivity = FALSE; + dcb.fNull = FALSE; // Do not ignore NULL characters. + dcb.fAbortOnError = FALSE; // Ignore serial framing errors. + if (!::SetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set up timeouts so that the serial port will behave similarly to a + // network socket. Reads wait for at least one byte, then return with + // whatever they have. Writes return once everything is out the door. + ::COMMTIMEOUTS timeouts; + timeouts.ReadIntervalTimeout = 1; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + if (!::SetCommTimeouts(handle, &timeouts)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // We're done. Take ownership of the serial port handle. + if (handle_service_.assign(impl, handle, ec)) + ::CloseHandle(handle); + return ec; +} + +asio::error_code win_iocp_serial_port_service::do_set_option( + win_iocp_serial_port_service::implementation_type& impl, + win_iocp_serial_port_service::store_function_type store, + const void* option, asio::error_code& ec) +{ + using namespace std; // For memcpy. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native_handle(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + if (store(option, dcb, ec)) + return ec; + + if (!::SetCommState(handle_service_.native_handle(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_serial_port_service::do_get_option( + const win_iocp_serial_port_service::implementation_type& impl, + win_iocp_serial_port_service::load_function_type load, + void* option, asio::error_code& ec) const +{ + using namespace std; // For memset. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native_handle(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + return load(option, dcb, ec); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_iocp_socket_service_base.ipp b/subprojects/asio/include/asio/detail/impl/win_iocp_socket_service_base.ipp new file mode 100644 index 000000000..6c478cd2f --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_iocp_socket_service_base.ipp @@ -0,0 +1,799 @@ +// +// detail/impl/win_iocp_socket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/win_iocp_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_iocp_socket_service_base::win_iocp_socket_service_base( + asio::io_context& io_context) + : io_context_(io_context), + iocp_service_(use_service(io_context)), + reactor_(0), + connect_ex_(0), + nt_set_info_(0), + mutex_(), + impl_list_(0) +{ +} + +void win_iocp_socket_service_base::base_shutdown() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + base_implementation_type* impl = impl_list_; + while (impl) + { + close_for_destruction(*impl); + impl = impl->next_; + } +} + +void win_iocp_socket_service_base::construct( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_socket_service_base::base_move_construct( + win_iocp_socket_service_base::base_implementation_type& impl, + win_iocp_socket_service_base::base_implementation_type& other_impl) +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + impl.cancel_token_ = other_impl.cancel_token_; + other_impl.cancel_token_.reset(); + +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_socket_service_base::base_move_assign( + win_iocp_socket_service_base::base_implementation_type& impl, + win_iocp_socket_service_base& other_service, + win_iocp_socket_service_base::base_implementation_type& other_impl) +{ + close_for_destruction(impl); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + impl.cancel_token_ = other_impl.cancel_token_; + other_impl.cancel_token_.reset(); + +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(other_service.mutex_); + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } +} + +void win_iocp_socket_service_base::destroy( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code win_iocp_socket_service_base::close( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), + "socket", &impl, impl.socket_, "close")); + + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->deregister_descriptor(impl.socket_, impl.reactor_data_, true); + + socket_ops::close(impl.socket_, impl.state_, false, ec); + + if (r) + r->cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = asio::error_code(); + } + + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + return ec; +} + +socket_type win_iocp_socket_service_base::release( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + return invalid_socket; + + cancel(impl, ec); + if (ec) + return invalid_socket; + + nt_set_info_fn fn = get_nt_set_info(); + if (fn == 0) + { + ec = asio::error::operation_not_supported; + return invalid_socket; + } + + HANDLE sock_as_handle = reinterpret_cast(impl.socket_); + ULONG_PTR iosb[2] = { 0, 0 }; + void* info[2] = { 0, 0 }; + if (fn(sock_as_handle, iosb, &info, sizeof(info), + 61 /* FileReplaceCompletionInformation */)) + { + ec = asio::error::operation_not_supported; + return invalid_socket; + } + + socket_type tmp = impl.socket_; + impl.socket_ = invalid_socket; + return tmp; +} + +asio::error_code win_iocp_socket_service_base::cancel( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((iocp_service_.context(), + "socket", &impl, impl.socket_, "cancel")); + + if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!cancel_io_ex(sock_as_handle, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } +#if defined(ASIO_ENABLE_CANCELIO) + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!::CancelIo(sock_as_handle)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } +#else // defined(ASIO_ENABLE_CANCELIO) + else + { + // Cancellation is not supported as CancelIo may not be used. + ec = asio::error::operation_not_supported; + } +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Cancel any operations started via the reactor. + if (!ec) + { + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->cancel_ops(impl.socket_, impl.reactor_data_); + } + + return ec; +} + +asio::error_code win_iocp_socket_service_base::do_open( + win_iocp_socket_service_base::base_implementation_type& impl, + int family, int type, int protocol, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(family, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + HANDLE sock_as_handle = reinterpret_cast(sock.get()); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_socket_service_base::do_assign( + win_iocp_socket_service_base::base_implementation_type& impl, + int type, socket_type native_socket, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + HANDLE sock_as_handle = reinterpret_cast(native_socket); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); + ec = asio::error_code(); + return ec; +} + +void win_iocp_socket_service_base::start_send_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, buffers, + static_cast(buffer_count), &bytes_transferred, flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_send_to_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + const socket_addr_type* addr, int addrlen, + socket_base::message_flags flags, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, flags, addr, addrlen, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_receive_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, &recv_flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_null_buffers_receive_op( + win_iocp_socket_service_base::base_implementation_type& impl, + socket_base::message_flags flags, reactor_op* op) +{ + if ((impl.state_ & socket_ops::stream_oriented) != 0) + { + // For stream sockets on Windows, we may issue a 0-byte overlapped + // WSARecv to wait until there is data available on the socket. + ::WSABUF buf = { 0, 0 }; + start_receive_op(impl, &buf, 1, flags, false, op); + } + else + { + start_reactor_op(impl, + (flags & socket_base::message_out_of_band) + ? select_reactor::except_op : select_reactor::read_op, + op); + } +} + +void win_iocp_socket_service_base::start_receive_from_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, + socket_base::message_flags flags, int* addrlen, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecvFrom(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, &recv_flags, addr, addrlen, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_accept_op( + win_iocp_socket_service_base::base_implementation_type& impl, + bool peer_is_open, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else if (peer_is_open) + iocp_service_.on_completion(op, asio::error::already_open); + else + { + asio::error_code ec; + new_socket.reset(socket_ops::socket(family, type, protocol, ec)); + if (new_socket.get() == invalid_socket) + iocp_service_.on_completion(op, ec); + else + { + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, + 0, address_length, address_length, &bytes_read, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + } + } +} + +void win_iocp_socket_service_base::restart_accept_op( + socket_type s, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op) +{ + new_socket.reset(); + iocp_service_.work_started(); + + asio::error_code ec; + new_socket.reset(socket_ops::socket(family, type, protocol, ec)); + if (new_socket.get() == invalid_socket) + iocp_service_.on_completion(op, ec); + else + { + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(s, new_socket.get(), output_buffer, + 0, address_length, address_length, &bytes_read, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_reactor_op( + win_iocp_socket_service_base::base_implementation_type& impl, + int op_type, reactor_op* op) +{ + select_reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if (is_open(impl)) + { + r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false, false); + return; + } + else + op->ec_ = asio::error::bad_descriptor; + + iocp_service_.post_immediate_completion(op, false); +} + +void win_iocp_socket_service_base::start_connect_op( + win_iocp_socket_service_base::base_implementation_type& impl, + int family, int type, const socket_addr_type* addr, + std::size_t addrlen, win_iocp_socket_connect_op_base* op) +{ + // If ConnectEx is available, use that. + if (family == ASIO_OS_DEF(AF_INET) + || family == ASIO_OS_DEF(AF_INET6)) + { + if (connect_ex_fn connect_ex = get_connect_ex(impl, type)) + { + union address_union + { + socket_addr_type base; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } a; + + using namespace std; // For memset. + memset(&a, 0, sizeof(a)); + a.base.sa_family = family; + + socket_ops::bind(impl.socket_, &a.base, + family == ASIO_OS_DEF(AF_INET) + ? sizeof(a.v4) : sizeof(a.v6), op->ec_); + if (op->ec_ && op->ec_ != asio::error::invalid_argument) + { + iocp_service_.post_immediate_completion(op, false); + return; + } + + op->connect_ex_ = true; + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + BOOL result = connect_ex(impl.socket_, + addr, static_cast(addrlen), 0, 0, 0, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + return; + } + } + + // Otherwise, fall back to a reactor-based implementation. + select_reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if ((impl.state_ & socket_ops::non_blocking) != 0 + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + r.start_op(select_reactor::connect_op, impl.socket_, + impl.reactor_data_, op, false, false); + return; + } + } + } + + r.post_immediate_completion(op, false); +} + +void win_iocp_socket_service_base::close_for_destruction( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), + "socket", &impl, impl.socket_, "close")); + + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->deregister_descriptor(impl.socket_, impl.reactor_data_, true); + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + + if (r) + r->cleanup_descriptor_data(impl.reactor_data_); + } + + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) +} + +void win_iocp_socket_service_base::update_cancellation_thread_id( + win_iocp_socket_service_base::base_implementation_type& impl) +{ +#if defined(ASIO_ENABLE_CANCELIO) + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +#else // defined(ASIO_ENABLE_CANCELIO) + (void)impl; +#endif // defined(ASIO_ENABLE_CANCELIO) +} + +select_reactor& win_iocp_socket_service_base::get_reactor() +{ + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!r) + { + r = &(use_service(io_context_)); + interlocked_exchange_pointer(reinterpret_cast(&reactor_), r); + } + return *r; +} + +win_iocp_socket_service_base::connect_ex_fn +win_iocp_socket_service_base::get_connect_ex( + win_iocp_socket_service_base::base_implementation_type& impl, int type) +{ +#if defined(ASIO_DISABLE_CONNECTEX) + (void)impl; + (void)type; + return 0; +#else // defined(ASIO_DISABLE_CONNECTEX) + if (type != ASIO_OS_DEF(SOCK_STREAM) + && type != ASIO_OS_DEF(SOCK_SEQPACKET)) + return 0; + + void* ptr = interlocked_compare_exchange_pointer(&connect_ex_, 0, 0); + if (!ptr) + { + GUID guid = { 0x25a207b9, 0xddf3, 0x4660, + { 0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e } }; + + DWORD bytes = 0; + if (::WSAIoctl(impl.socket_, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), &ptr, sizeof(ptr), &bytes, 0, 0) != 0) + { + // Set connect_ex_ to a special value to indicate that ConnectEx is + // unavailable. That way we won't bother trying to look it up again. + ptr = this; + } + + interlocked_exchange_pointer(&connect_ex_, ptr); + } + + return reinterpret_cast(ptr == this ? 0 : ptr); +#endif // defined(ASIO_DISABLE_CONNECTEX) +} + +win_iocp_socket_service_base::nt_set_info_fn +win_iocp_socket_service_base::get_nt_set_info() +{ + void* ptr = interlocked_compare_exchange_pointer(&nt_set_info_, 0, 0); + if (!ptr) + { + if (HMODULE h = ::GetModuleHandleA("NTDLL.DLL")) + ptr = reinterpret_cast(GetProcAddress(h, "NtSetInformationFile")); + + // On failure, set nt_set_info_ to a special value to indicate that the + // NtSetInformationFile function is unavailable. That way we won't bother + // trying to look it up again. + interlocked_exchange_pointer(&nt_set_info_, ptr ? ptr : this); + } + + return reinterpret_cast(ptr == this ? 0 : ptr); +} + +void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer( + void** dest, void* exch, void* cmp) +{ +#if defined(_M_IX86) + return reinterpret_cast(InterlockedCompareExchange( + reinterpret_cast(dest), reinterpret_cast(exch), + reinterpret_cast(cmp))); +#else + return InterlockedCompareExchangePointer(dest, exch, cmp); +#endif +} + +void* win_iocp_socket_service_base::interlocked_exchange_pointer( + void** dest, void* val) +{ +#if defined(_M_IX86) + return reinterpret_cast(InterlockedExchange( + reinterpret_cast(dest), reinterpret_cast(val))); +#else + return InterlockedExchangePointer(dest, val); +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_mutex.ipp b/subprojects/asio/include/asio/detail/impl/win_mutex.ipp new file mode 100644 index 000000000..dc58a12cd --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_mutex.ipp @@ -0,0 +1,84 @@ +// +// detail/impl/win_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_MUTEX_IPP +#define ASIO_DETAIL_IMPL_WIN_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_mutex.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_mutex::win_mutex() +{ + int error = do_init(); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "mutex"); +} + +int win_mutex::do_init() +{ +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# elif defined(ASIO_WINDOWS_APP) + if (!::InitializeCriticalSectionEx(&crit_section_, 0, 0)) + return ::GetLastError(); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + return ::GetLastError(); +# endif + return 0; +#else + __try + { +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# elif defined(ASIO_WINDOWS_APP) + if (!::InitializeCriticalSectionEx(&crit_section_, 0, 0)) + return ::GetLastError(); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + return ::GetLastError(); +# endif + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + return ERROR_OUTOFMEMORY; + } + + return 0; +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_MUTEX_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_object_handle_service.ipp b/subprojects/asio/include/asio/detail/impl/win_object_handle_service.ipp new file mode 100644 index 000000000..c5e59d1be --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_object_handle_service.ipp @@ -0,0 +1,449 @@ +// +// detail/impl/win_object_handle_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2011 Boris Schaeling (boris@highscore.de) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#include "asio/detail/win_object_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_object_handle_service::win_object_handle_service( + asio::io_context& io_context) + : service_base(io_context), + io_context_(asio::use_service(io_context)), + mutex_(), + impl_list_(0), + shutdown_(false) +{ +} + +void win_object_handle_service::shutdown() +{ + mutex::scoped_lock lock(mutex_); + + // Setting this flag to true prevents new objects from being registered, and + // new asynchronous wait operations from being started. We only need to worry + // about cleaning up the operations that are currently in progress. + shutdown_ = true; + + op_queue ops; + for (implementation_type* impl = impl_list_; impl; impl = impl->next_) + ops.push(impl->op_queue_); + + lock.unlock(); + + io_context_.abandon_operations(ops); +} + +void win_object_handle_service::construct( + win_object_handle_service::implementation_type& impl) +{ + impl.handle_ = INVALID_HANDLE_VALUE; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + impl.owner_ = this; + + // Insert implementation into linked list of all implementations. + mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } +} + +void win_object_handle_service::move_construct( + win_object_handle_service::implementation_type& impl, + win_object_handle_service::implementation_type& other_impl) +{ + mutex::scoped_lock lock(mutex_); + + // Insert implementation into linked list of all implementations. + if (!shutdown_) + { + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } + + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + impl.wait_handle_ = other_impl.wait_handle_; + other_impl.wait_handle_ = INVALID_HANDLE_VALUE; + impl.op_queue_.push(other_impl.op_queue_); + impl.owner_ = this; + + // We must not hold the lock while calling UnregisterWaitEx. This is because + // the registered callback function might be invoked while we are waiting for + // UnregisterWaitEx to complete. + lock.unlock(); + + if (impl.wait_handle_ != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE); + + if (!impl.op_queue_.empty()) + register_wait_callback(impl, lock); +} + +void win_object_handle_service::move_assign( + win_object_handle_service::implementation_type& impl, + win_object_handle_service& other_service, + win_object_handle_service::implementation_type& other_impl) +{ + asio::error_code ignored_ec; + close(impl, ignored_ec); + + mutex::scoped_lock lock(mutex_); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + impl.wait_handle_ = other_impl.wait_handle_; + other_impl.wait_handle_ = INVALID_HANDLE_VALUE; + impl.op_queue_.push(other_impl.op_queue_); + impl.owner_ = this; + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } + + // We must not hold the lock while calling UnregisterWaitEx. This is because + // the registered callback function might be invoked while we are waiting for + // UnregisterWaitEx to complete. + lock.unlock(); + + if (impl.wait_handle_ != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE); + + if (!impl.op_queue_.empty()) + register_wait_callback(impl, lock); +} + +void win_object_handle_service::destroy( + win_object_handle_service::implementation_type& impl) +{ + mutex::scoped_lock lock(mutex_); + + // Remove implementation from linked list of all implementations. + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((io_context_.context(), "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "close")); + + HANDLE wait_handle = impl.wait_handle_; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + + op_queue ops; + while (wait_op* op = impl.op_queue_.front()) + { + op->ec_ = asio::error::operation_aborted; + impl.op_queue_.pop(); + ops.push(op); + } + + // We must not hold the lock while calling UnregisterWaitEx. This is + // because the registered callback function might be invoked while we are + // waiting for UnregisterWaitEx to complete. + lock.unlock(); + + if (wait_handle != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); + + ::CloseHandle(impl.handle_); + impl.handle_ = INVALID_HANDLE_VALUE; + + io_context_.post_deferred_completions(ops); + } +} + +asio::error_code win_object_handle_service::assign( + win_object_handle_service::implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + impl.handle_ = handle; + ec = asio::error_code(); + return ec; +} + +asio::error_code win_object_handle_service::close( + win_object_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((io_context_.context(), "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "close")); + + mutex::scoped_lock lock(mutex_); + + HANDLE wait_handle = impl.wait_handle_; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + + op_queue completed_ops; + while (wait_op* op = impl.op_queue_.front()) + { + impl.op_queue_.pop(); + op->ec_ = asio::error::operation_aborted; + completed_ops.push(op); + } + + // We must not hold the lock while calling UnregisterWaitEx. This is + // because the registered callback function might be invoked while we are + // waiting for UnregisterWaitEx to complete. + lock.unlock(); + + if (wait_handle != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); + + if (::CloseHandle(impl.handle_)) + { + impl.handle_ = INVALID_HANDLE_VALUE; + ec = asio::error_code(); + } + else + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + + io_context_.post_deferred_completions(completed_ops); + } + else + { + ec = asio::error_code(); + } + + return ec; +} + +asio::error_code win_object_handle_service::cancel( + win_object_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((io_context_.context(), "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "cancel")); + + mutex::scoped_lock lock(mutex_); + + HANDLE wait_handle = impl.wait_handle_; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + + op_queue completed_ops; + while (wait_op* op = impl.op_queue_.front()) + { + op->ec_ = asio::error::operation_aborted; + impl.op_queue_.pop(); + completed_ops.push(op); + } + + // We must not hold the lock while calling UnregisterWaitEx. This is + // because the registered callback function might be invoked while we are + // waiting for UnregisterWaitEx to complete. + lock.unlock(); + + if (wait_handle != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); + + ec = asio::error_code(); + + io_context_.post_deferred_completions(completed_ops); + } + else + { + ec = asio::error::bad_descriptor; + } + + return ec; +} + +void win_object_handle_service::wait( + win_object_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + switch (::WaitForSingleObject(impl.handle_, INFINITE)) + { + case WAIT_FAILED: + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + break; + } + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + default: + ec = asio::error_code(); + break; + } +} + +void win_object_handle_service::start_wait_op( + win_object_handle_service::implementation_type& impl, wait_op* op) +{ + io_context_.work_started(); + + if (is_open(impl)) + { + mutex::scoped_lock lock(mutex_); + + if (!shutdown_) + { + impl.op_queue_.push(op); + + // Only the first operation to be queued gets to register a wait callback. + // Subsequent operations have to wait for the first to finish. + if (impl.op_queue_.front() == op) + register_wait_callback(impl, lock); + } + else + { + lock.unlock(); + io_context_.post_deferred_completion(op); + } + } + else + { + op->ec_ = asio::error::bad_descriptor; + io_context_.post_deferred_completion(op); + } +} + +void win_object_handle_service::register_wait_callback( + win_object_handle_service::implementation_type& impl, + mutex::scoped_lock& lock) +{ + lock.lock(); + + if (!RegisterWaitForSingleObject(&impl.wait_handle_, + impl.handle_, &win_object_handle_service::wait_callback, + &impl, INFINITE, WT_EXECUTEONLYONCE)) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + + op_queue completed_ops; + while (wait_op* op = impl.op_queue_.front()) + { + op->ec_ = ec; + impl.op_queue_.pop(); + completed_ops.push(op); + } + + lock.unlock(); + io_context_.post_deferred_completions(completed_ops); + } +} + +void win_object_handle_service::wait_callback(PVOID param, BOOLEAN) +{ + implementation_type* impl = static_cast(param); + mutex::scoped_lock lock(impl->owner_->mutex_); + + if (impl->wait_handle_ != INVALID_HANDLE_VALUE) + { + ::UnregisterWaitEx(impl->wait_handle_, NULL); + impl->wait_handle_ = INVALID_HANDLE_VALUE; + } + + if (wait_op* op = impl->op_queue_.front()) + { + op_queue completed_ops; + + op->ec_ = asio::error_code(); + impl->op_queue_.pop(); + completed_ops.push(op); + + if (!impl->op_queue_.empty()) + { + if (!RegisterWaitForSingleObject(&impl->wait_handle_, + impl->handle_, &win_object_handle_service::wait_callback, + param, INFINITE, WT_EXECUTEONLYONCE)) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + + while ((op = impl->op_queue_.front()) != 0) + { + op->ec_ = ec; + impl->op_queue_.pop(); + completed_ops.push(op); + } + } + } + + io_context_impl& ioc = impl->owner_->io_context_; + lock.unlock(); + ioc.post_deferred_completions(completed_ops); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#endif // ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_static_mutex.ipp b/subprojects/asio/include/asio/detail/impl/win_static_mutex.ipp new file mode 100644 index 000000000..42089bd5c --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_static_mutex.ipp @@ -0,0 +1,136 @@ +// +// detail/impl/win_static_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_STATIC_MUTEX_IPP +#define ASIO_DETAIL_IMPL_WIN_STATIC_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_static_mutex.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void win_static_mutex::init() +{ + int error = do_init(); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "static_mutex"); +} + +int win_static_mutex::do_init() +{ + using namespace std; // For sprintf. + wchar_t mutex_name[128]; +#if defined(ASIO_HAS_SECURE_RTL) + swprintf_s( +#else // defined(ASIO_HAS_SECURE_RTL) + _snwprintf( +#endif // defined(ASIO_HAS_SECURE_RTL) + mutex_name, 128, L"asio-58CCDC44-6264-4842-90C2-F3C545CB8AA7-%u-%p", + static_cast(::GetCurrentProcessId()), this); + +#if defined(ASIO_WINDOWS_APP) + HANDLE mutex = ::CreateMutexExW(0, mutex_name, CREATE_MUTEX_INITIAL_OWNER, 0); +#else // defined(ASIO_WINDOWS_APP) + HANDLE mutex = ::CreateMutexW(0, TRUE, mutex_name); +#endif // defined(ASIO_WINDOWS_APP) + DWORD last_error = ::GetLastError(); + if (mutex == 0) + return ::GetLastError(); + + if (last_error == ERROR_ALREADY_EXISTS) + { +#if defined(ASIO_WINDOWS_APP) + ::WaitForSingleObjectEx(mutex, INFINITE, false); +#else // defined(ASIO_WINDOWS_APP) + ::WaitForSingleObject(mutex, INFINITE); +#endif // defined(ASIO_WINDOWS_APP) + } + + if (initialised_) + { + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return 0; + } + +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + { + last_error = ::GetLastError(); + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return last_error; + } +# endif +#else + __try + { +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# elif defined(ASIO_WINDOWS_APP) + if (!::InitializeCriticalSectionEx(&crit_section_, 0, 0)) + { + last_error = ::GetLastError(); + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return last_error; + } +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + { + last_error = ::GetLastError(); + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return last_error; + } +# endif + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return ERROR_OUTOFMEMORY; + } +#endif + + initialised_ = true; + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_STATIC_MUTEX_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_thread.ipp b/subprojects/asio/include/asio/detail/impl/win_thread.ipp new file mode 100644 index 000000000..ed6dbafe0 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_thread.ipp @@ -0,0 +1,150 @@ +// +// detail/impl/win_thread.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_THREAD_IPP +#define ASIO_DETAIL_IMPL_WIN_THREAD_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_APP) \ + && !defined(UNDER_CE) + +#include +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_thread.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_thread::~win_thread() +{ + ::CloseHandle(thread_); + + // The exit_event_ handle is deliberately allowed to leak here since it + // is an error for the owner of an internal thread not to join() it. +} + +void win_thread::join() +{ + HANDLE handles[2] = { exit_event_, thread_ }; + ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + ::CloseHandle(exit_event_); + if (terminate_threads()) + { + ::TerminateThread(thread_, 0); + } + else + { + ::QueueUserAPC(apc_function, thread_, 0); + ::WaitForSingleObject(thread_, INFINITE); + } +} + +std::size_t win_thread::hardware_concurrency() +{ + SYSTEM_INFO system_info; + ::GetSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; +} + +void win_thread::start_thread(func_base* arg, unsigned int stack_size) +{ + ::HANDLE entry_event = 0; + arg->entry_event_ = entry_event = ::CreateEventW(0, true, false, 0); + if (!entry_event) + { + DWORD last_error = ::GetLastError(); + delete arg; + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread.entry_event"); + } + + arg->exit_event_ = exit_event_ = ::CreateEventW(0, true, false, 0); + if (!exit_event_) + { + DWORD last_error = ::GetLastError(); + delete arg; + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread.exit_event"); + } + + unsigned int thread_id = 0; + thread_ = reinterpret_cast(::_beginthreadex(0, + stack_size, win_thread_function, arg, 0, &thread_id)); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + delete arg; + if (entry_event) + ::CloseHandle(entry_event); + if (exit_event_) + ::CloseHandle(exit_event_); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } + + if (entry_event) + { + ::WaitForSingleObject(entry_event, INFINITE); + ::CloseHandle(entry_event); + } +} + +unsigned int __stdcall win_thread_function(void* arg) +{ + win_thread::auto_func_base_ptr func = { + static_cast(arg) }; + + ::SetEvent(func.ptr->entry_event_); + + func.ptr->run(); + + // Signal that the thread has finished its work, but rather than returning go + // to sleep to put the thread into a well known state. If the thread is being + // joined during global object destruction then it may be killed using + // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx + // call will be interrupted using QueueUserAPC and the thread will shut down + // cleanly. + HANDLE exit_event = func.ptr->exit_event_; + delete func.ptr; + func.ptr = 0; + ::SetEvent(exit_event); + ::SleepEx(INFINITE, TRUE); + + return 0; +} + +#if defined(WINVER) && (WINVER < 0x0500) +void __stdcall apc_function(ULONG) {} +#else +void __stdcall apc_function(ULONG_PTR) {} +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_APP) + // && !defined(UNDER_CE) + +#endif // ASIO_DETAIL_IMPL_WIN_THREAD_IPP diff --git a/subprojects/asio/include/asio/detail/impl/win_tss_ptr.ipp b/subprojects/asio/include/asio/detail/impl/win_tss_ptr.ipp new file mode 100644 index 000000000..61df0ce26 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/win_tss_ptr.ipp @@ -0,0 +1,57 @@ +// +// detail/impl/win_tss_ptr.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP +#define ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_tss_ptr.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +DWORD win_tss_ptr_create() +{ +#if defined(UNDER_CE) + const DWORD out_of_indexes = 0xFFFFFFFF; +#else + const DWORD out_of_indexes = TLS_OUT_OF_INDEXES; +#endif + + DWORD tss_key = ::TlsAlloc(); + if (tss_key == out_of_indexes) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "tss"); + } + return tss_key; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP diff --git a/subprojects/asio/include/asio/detail/impl/winrt_ssocket_service_base.ipp b/subprojects/asio/include/asio/detail/impl/winrt_ssocket_service_base.ipp new file mode 100644 index 000000000..288ca8b13 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/winrt_ssocket_service_base.ipp @@ -0,0 +1,629 @@ +// +// detail/impl/winrt_ssocket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/winrt_ssocket_service_base.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/detail/winrt_utils.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +winrt_ssocket_service_base::winrt_ssocket_service_base( + asio::io_context& io_context) + : io_context_(use_service(io_context)), + async_manager_(use_service(io_context)), + mutex_(), + impl_list_(0) +{ +} + +void winrt_ssocket_service_base::base_shutdown() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + base_implementation_type* impl = impl_list_; + while (impl) + { + asio::error_code ignored_ec; + close(*impl, ignored_ec); + impl = impl->next_; + } +} + +void winrt_ssocket_service_base::construct( + winrt_ssocket_service_base::base_implementation_type& impl) +{ + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void winrt_ssocket_service_base::base_move_construct( + winrt_ssocket_service_base::base_implementation_type& impl, + winrt_ssocket_service_base::base_implementation_type& other_impl) +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = nullptr; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void winrt_ssocket_service_base::base_move_assign( + winrt_ssocket_service_base::base_implementation_type& impl, + winrt_ssocket_service_base& other_service, + winrt_ssocket_service_base::base_implementation_type& other_impl) +{ + asio::error_code ignored_ec; + close(impl, ignored_ec); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = nullptr; + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(other_service.mutex_); + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } +} + +void winrt_ssocket_service_base::destroy( + winrt_ssocket_service_base::base_implementation_type& impl) +{ + asio::error_code ignored_ec; + close(impl, ignored_ec); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code winrt_ssocket_service_base::close( + winrt_ssocket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (impl.socket_) + { + delete impl.socket_; + impl.socket_ = nullptr; + } + + ec = asio::error_code(); + return ec; +} + +winrt_ssocket_service_base::native_handle_type +winrt_ssocket_service_base::release( + winrt_ssocket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + return nullptr; + + cancel(impl, ec); + if (ec) + return nullptr; + + native_handle_type tmp = impl.socket_; + impl.socket_ = nullptr; + return tmp; +} + +std::size_t winrt_ssocket_service_base::do_get_endpoint( + const base_implementation_type& impl, bool local, + void* addr, std::size_t addr_len, asio::error_code& ec) const +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return addr_len; + } + + try + { + std::string addr_string = winrt_utils::string(local + ? impl.socket_->Information->LocalAddress->CanonicalName + : impl.socket_->Information->RemoteAddress->CanonicalName); + unsigned short port = winrt_utils::integer(local + ? impl.socket_->Information->LocalPort + : impl.socket_->Information->RemotePort); + unsigned long scope = 0; + + switch (reinterpret_cast(addr)->sa_family) + { + case ASIO_OS_DEF(AF_INET): + if (addr_len < sizeof(sockaddr_in4_type)) + { + ec = asio::error::invalid_argument; + return addr_len; + } + else + { + socket_ops::inet_pton(ASIO_OS_DEF(AF_INET), addr_string.c_str(), + &reinterpret_cast(addr)->sin_addr, &scope, ec); + reinterpret_cast(addr)->sin_port + = socket_ops::host_to_network_short(port); + ec = asio::error_code(); + return sizeof(sockaddr_in4_type); + } + case ASIO_OS_DEF(AF_INET6): + if (addr_len < sizeof(sockaddr_in6_type)) + { + ec = asio::error::invalid_argument; + return addr_len; + } + else + { + socket_ops::inet_pton(ASIO_OS_DEF(AF_INET6), addr_string.c_str(), + &reinterpret_cast(addr)->sin6_addr, &scope, ec); + reinterpret_cast(addr)->sin6_port + = socket_ops::host_to_network_short(port); + ec = asio::error_code(); + return sizeof(sockaddr_in6_type); + } + default: + ec = asio::error::address_family_not_supported; + return addr_len; + } + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return addr_len; + } +} + +asio::error_code winrt_ssocket_service_base::do_set_option( + winrt_ssocket_service_base::base_implementation_type& impl, + int level, int optname, const void* optval, + std::size_t optlen, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + try + { + if (level == ASIO_OS_DEF(SOL_SOCKET) + && optname == ASIO_OS_DEF(SO_KEEPALIVE)) + { + if (optlen == sizeof(int)) + { + int value = 0; + std::memcpy(&value, optval, optlen); + impl.socket_->Control->KeepAlive = !!value; + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else if (level == ASIO_OS_DEF(IPPROTO_TCP) + && optname == ASIO_OS_DEF(TCP_NODELAY)) + { + if (optlen == sizeof(int)) + { + int value = 0; + std::memcpy(&value, optval, optlen); + impl.socket_->Control->NoDelay = !!value; + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else + { + ec = asio::error::invalid_argument; + } + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } + + return ec; +} + +void winrt_ssocket_service_base::do_get_option( + const winrt_ssocket_service_base::base_implementation_type& impl, + int level, int optname, void* optval, + std::size_t* optlen, asio::error_code& ec) const +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return; + } + + try + { + if (level == ASIO_OS_DEF(SOL_SOCKET) + && optname == ASIO_OS_DEF(SO_KEEPALIVE)) + { + if (*optlen >= sizeof(int)) + { + int value = impl.socket_->Control->KeepAlive ? 1 : 0; + std::memcpy(optval, &value, sizeof(int)); + *optlen = sizeof(int); + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else if (level == ASIO_OS_DEF(IPPROTO_TCP) + && optname == ASIO_OS_DEF(TCP_NODELAY)) + { + if (*optlen >= sizeof(int)) + { + int value = impl.socket_->Control->NoDelay ? 1 : 0; + std::memcpy(optval, &value, sizeof(int)); + *optlen = sizeof(int); + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else + { + ec = asio::error::invalid_argument; + } + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } +} + +asio::error_code winrt_ssocket_service_base::do_connect( + winrt_ssocket_service_base::base_implementation_type& impl, + const void* addr, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + char addr_string[max_addr_v6_str_len]; + unsigned short port; + switch (reinterpret_cast(addr)->sa_family) + { + case ASIO_OS_DEF(AF_INET): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET), + &reinterpret_cast(addr)->sin_addr, + addr_string, sizeof(addr_string), 0, ec); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin_port); + break; + case ASIO_OS_DEF(AF_INET6): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET6), + &reinterpret_cast(addr)->sin6_addr, + addr_string, sizeof(addr_string), 0, ec); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin6_port); + break; + default: + ec = asio::error::address_family_not_supported; + return ec; + } + + if (!ec) try + { + async_manager_.sync(impl.socket_->ConnectAsync( + ref new Windows::Networking::HostName( + winrt_utils::string(addr_string)), + winrt_utils::string(port)), ec); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } + + return ec; +} + +void winrt_ssocket_service_base::start_connect_op( + winrt_ssocket_service_base::base_implementation_type& impl, + const void* addr, winrt_async_op* op, bool is_continuation) +{ + if (!is_open(impl)) + { + op->ec_ = asio::error::bad_descriptor; + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + char addr_string[max_addr_v6_str_len]; + unsigned short port = 0; + switch (reinterpret_cast(addr)->sa_family) + { + case ASIO_OS_DEF(AF_INET): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET), + &reinterpret_cast(addr)->sin_addr, + addr_string, sizeof(addr_string), 0, op->ec_); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin_port); + break; + case ASIO_OS_DEF(AF_INET6): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET6), + &reinterpret_cast(addr)->sin6_addr, + addr_string, sizeof(addr_string), 0, op->ec_); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin6_port); + break; + default: + op->ec_ = asio::error::address_family_not_supported; + break; + } + + if (op->ec_) + { + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + try + { + async_manager_.async(impl.socket_->ConnectAsync( + ref new Windows::Networking::HostName( + winrt_utils::string(addr_string)), + winrt_utils::string(port)), op); + } + catch (Platform::Exception^ e) + { + op->ec_ = asio::error_code( + e->HResult, asio::system_category()); + io_context_.post_immediate_completion(op, is_continuation); + } +} + +std::size_t winrt_ssocket_service_base::do_send( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::const_buffer& data, + socket_base::message_flags flags, asio::error_code& ec) +{ + if (flags) + { + ec = asio::error::operation_not_supported; + return 0; + } + + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + return async_manager_.sync( + impl.socket_->OutputStream->WriteAsync(bufs.buffers()[0]), ec); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return 0; + } +} + +void winrt_ssocket_service_base::start_send_op( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::const_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, bool is_continuation) +{ + if (flags) + { + op->ec_ = asio::error::operation_not_supported; + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + if (!is_open(impl)) + { + op->ec_ = asio::error::bad_descriptor; + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + async_manager_.async( + impl.socket_->OutputStream->WriteAsync(bufs.buffers()[0]), op); + } + catch (Platform::Exception^ e) + { + op->ec_ = asio::error_code(e->HResult, + asio::system_category()); + io_context_.post_immediate_completion(op, is_continuation); + } +} + +std::size_t winrt_ssocket_service_base::do_receive( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::mutable_buffer& data, + socket_base::message_flags flags, asio::error_code& ec) +{ + if (flags) + { + ec = asio::error::operation_not_supported; + return 0; + } + + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + async_manager_.sync( + impl.socket_->InputStream->ReadAsync( + bufs.buffers()[0], bufs.buffers()[0]->Capacity, + Windows::Storage::Streams::InputStreamOptions::Partial), ec); + + std::size_t bytes_transferred = bufs.buffers()[0]->Length; + if (bytes_transferred == 0 && !ec) + { + ec = asio::error::eof; + } + + return bytes_transferred; + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return 0; + } +} + +void winrt_ssocket_service_base::start_receive_op( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::mutable_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, + bool is_continuation) +{ + if (flags) + { + op->ec_ = asio::error::operation_not_supported; + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + if (!is_open(impl)) + { + op->ec_ = asio::error::bad_descriptor; + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + io_context_.post_immediate_completion(op, is_continuation); + return; + } + + async_manager_.async( + impl.socket_->InputStream->ReadAsync( + bufs.buffers()[0], bufs.buffers()[0]->Capacity, + Windows::Storage::Streams::InputStreamOptions::Partial), op); + } + catch (Platform::Exception^ e) + { + op->ec_ = asio::error_code(e->HResult, + asio::system_category()); + io_context_.post_immediate_completion(op, is_continuation); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP diff --git a/subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.hpp b/subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.hpp new file mode 100644 index 000000000..856378feb --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.hpp @@ -0,0 +1,92 @@ +// +// detail/impl/winrt_timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_HPP +#define ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void winrt_timer_scheduler::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void winrt_timer_scheduler::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void winrt_timer_scheduler::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + io_context_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + io_context_.work_started(); + if (earliest) + event_.signal(lock); +} + +template +std::size_t winrt_timer_scheduler::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + io_context_.post_deferred_completions(ops); + return n; +} + +template +void winrt_timer_scheduler::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(to, ops); + queue.move_timer(to, from); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_HPP diff --git a/subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.ipp b/subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.ipp new file mode 100644 index 000000000..ef2139965 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/winrt_timer_scheduler.ipp @@ -0,0 +1,122 @@ +// +// detail/impl/winrt_timer_scheduler.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_IPP +#define ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/winrt_timer_scheduler.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +winrt_timer_scheduler::winrt_timer_scheduler( + asio::io_context& io_context) + : asio::detail::service_base(io_context), + io_context_(use_service(io_context)), + mutex_(), + event_(), + timer_queues_(), + thread_(0), + stop_thread_(false), + shutdown_(false) +{ + thread_ = new asio::detail::thread( + bind_handler(&winrt_timer_scheduler::call_run_thread, this)); +} + +winrt_timer_scheduler::~winrt_timer_scheduler() +{ + shutdown(); +} + +void winrt_timer_scheduler::shutdown() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + event_.signal(lock); + lock.unlock(); + + if (thread_) + { + thread_->join(); + delete thread_; + thread_ = 0; + } + + op_queue ops; + timer_queues_.get_all_timers(ops); + io_context_.abandon_operations(ops); +} + +void winrt_timer_scheduler::notify_fork(asio::io_context::fork_event) +{ +} + +void winrt_timer_scheduler::init_task() +{ +} + +void winrt_timer_scheduler::run_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + const long max_wait_duration = 5 * 60 * 1000000; + long wait_duration = timer_queues_.wait_duration_usec(max_wait_duration); + event_.wait_for_usec(lock, wait_duration); + event_.clear(lock); + op_queue ops; + timer_queues_.get_ready_timers(ops); + if (!ops.empty()) + { + lock.unlock(); + io_context_.post_deferred_completions(ops); + lock.lock(); + } + } +} + +void winrt_timer_scheduler::call_run_thread(winrt_timer_scheduler* scheduler) +{ + scheduler->run_thread(); +} + +void winrt_timer_scheduler::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void winrt_timer_scheduler::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_IPP diff --git a/subprojects/asio/include/asio/detail/impl/winsock_init.ipp b/subprojects/asio/include/asio/detail/impl/winsock_init.ipp new file mode 100644 index 000000000..da4b0c0b4 --- /dev/null +++ b/subprojects/asio/include/asio/detail/impl/winsock_init.ipp @@ -0,0 +1,82 @@ +// +// detail/impl/winsock_init.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP +#define ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void winsock_init_base::startup(data& d, + unsigned char major, unsigned char minor) +{ + if (::InterlockedIncrement(&d.init_count_) == 1) + { + WSADATA wsa_data; + long result = ::WSAStartup(MAKEWORD(major, minor), &wsa_data); + ::InterlockedExchange(&d.result_, result); + } +} + +void winsock_init_base::manual_startup(data& d) +{ + if (::InterlockedIncrement(&d.init_count_) == 1) + { + ::InterlockedExchange(&d.result_, 0); + } +} + +void winsock_init_base::cleanup(data& d) +{ + if (::InterlockedDecrement(&d.init_count_) == 0) + { + ::WSACleanup(); + } +} + +void winsock_init_base::manual_cleanup(data& d) +{ + ::InterlockedDecrement(&d.init_count_); +} + +void winsock_init_base::throw_on_error(data& d) +{ + long result = ::InterlockedExchangeAdd(&d.result_, 0); + if (result != 0) + { + asio::error_code ec(result, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "winsock"); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP diff --git a/subprojects/asio/include/asio/detail/io_control.hpp b/subprojects/asio/include/asio/detail/io_control.hpp new file mode 100644 index 000000000..12f35e096 --- /dev/null +++ b/subprojects/asio/include/asio/detail/io_control.hpp @@ -0,0 +1,84 @@ +// +// detail/io_control.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IO_CONTROL_HPP +#define ASIO_DETAIL_IO_CONTROL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace io_control { + +// I/O control command for getting number of bytes available. +class bytes_readable +{ +public: + // Default constructor. + bytes_readable() + : value_(0) + { + } + + // Construct with a specific command value. + bytes_readable(std::size_t value) + : value_(static_cast(value)) + { + } + + // Get the name of the IO control command. + int name() const + { + return static_cast(ASIO_OS_DEF(FIONREAD)); + } + + // Set the value of the I/O control command. + void set(std::size_t value) + { + value_ = static_cast(value); + } + + // Get the current value of the I/O control command. + std::size_t get() const + { + return static_cast(value_); + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +} // namespace io_control +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IO_CONTROL_HPP diff --git a/subprojects/asio/include/asio/detail/is_buffer_sequence.hpp b/subprojects/asio/include/asio/detail/is_buffer_sequence.hpp new file mode 100644 index 000000000..69f9bbdf2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/is_buffer_sequence.hpp @@ -0,0 +1,263 @@ +// +// detail/is_buffer_sequence.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IS_BUFFER_SEQUENCE_HPP +#define ASIO_DETAIL_IS_BUFFER_SEQUENCE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class mutable_buffer; +class const_buffer; + +namespace detail { + +struct buffer_sequence_memfns_base +{ + void begin(); + void end(); + void size(); + void max_size(); + void capacity(); + void data(); + void prepare(); + void commit(); + void consume(); +}; + +template +struct buffer_sequence_memfns_derived + : T, buffer_sequence_memfns_base +{ +}; + +template +struct buffer_sequence_memfns_check +{ +}; + +template +char (&buffer_sequence_begin_helper(...))[2]; + +#if defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_begin_helper(T* t, + typename enable_if::value>::type*); + +#else // defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_begin_helper(T* t, + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::begin>*); + +#endif // defined(ASIO_HAS_DECLTYPE) + +template +char (&buffer_sequence_end_helper(...))[2]; + +#if defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_end_helper(T* t, + typename enable_if::value>::type*); + +#else // defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_end_helper(T* t, + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::end>*); + +#endif // defined(ASIO_HAS_DECLTYPE) + +template +char (&size_memfn_helper(...))[2]; + +template +char size_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::size>*); + +template +char (&max_size_memfn_helper(...))[2]; + +template +char max_size_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::max_size>*); + +template +char (&capacity_memfn_helper(...))[2]; + +template +char capacity_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::capacity>*); + +template +char (&data_memfn_helper(...))[2]; + +template +char data_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::data>*); + +template +char (&prepare_memfn_helper(...))[2]; + +template +char prepare_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::prepare>*); + +template +char (&commit_memfn_helper(...))[2]; + +template +char commit_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::commit>*); + +template +char (&consume_memfn_helper(...))[2]; + +template +char consume_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::consume>*); + +template +char (&buffer_sequence_element_type_helper(...))[2]; + +#if defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_element_type_helper(T* t, + typename enable_if::value>::type*); + +#else // defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_element_type_helper( + typename T::const_iterator*, + typename enable_if::value>::type*); + +#endif // defined(ASIO_HAS_DECLTYPE) + +template +char (&const_buffers_type_typedef_helper(...))[2]; + +template +char const_buffers_type_typedef_helper( + typename T::const_buffers_type*); + +template +char (&mutable_buffers_type_typedef_helper(...))[2]; + +template +char mutable_buffers_type_typedef_helper( + typename T::mutable_buffers_type*); + +template +struct is_buffer_sequence_class + : integral_constant(0)) != 1 && + sizeof(buffer_sequence_end_helper(0)) != 1 && + sizeof(buffer_sequence_element_type_helper(0, 0)) == 1> +{ +}; + +template +struct is_buffer_sequence + : conditional::value, + is_buffer_sequence_class, + false_type>::type +{ +}; + +template <> +struct is_buffer_sequence + : true_type +{ +}; + +template <> +struct is_buffer_sequence + : true_type +{ +}; + +template <> +struct is_buffer_sequence + : true_type +{ +}; + +template <> +struct is_buffer_sequence + : false_type +{ +}; + +template +struct is_dynamic_buffer_class + : integral_constant(0)) != 1 && + sizeof(max_size_memfn_helper(0)) != 1 && + sizeof(capacity_memfn_helper(0)) != 1 && + sizeof(data_memfn_helper(0)) != 1 && + sizeof(consume_memfn_helper(0)) != 1 && + sizeof(prepare_memfn_helper(0)) != 1 && + sizeof(commit_memfn_helper(0)) != 1 && + sizeof(const_buffers_type_typedef_helper(0)) == 1 && + sizeof(mutable_buffers_type_typedef_helper(0)) == 1> +{ +}; + +template +struct is_dynamic_buffer + : conditional::value, + is_dynamic_buffer_class, + false_type>::type +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IS_BUFFER_SEQUENCE_HPP diff --git a/subprojects/asio/include/asio/detail/is_executor.hpp b/subprojects/asio/include/asio/detail/is_executor.hpp new file mode 100644 index 000000000..4584dd04b --- /dev/null +++ b/subprojects/asio/include/asio/detail/is_executor.hpp @@ -0,0 +1,126 @@ +// +// detail/is_executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IS_EXECUTOR_HPP +#define ASIO_DETAIL_IS_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct executor_memfns_base +{ + void context(); + void on_work_started(); + void on_work_finished(); + void dispatch(); + void post(); + void defer(); +}; + +template +struct executor_memfns_derived + : T, executor_memfns_base +{ +}; + +template +struct executor_memfns_check +{ +}; + +template +char (&context_memfn_helper(...))[2]; + +template +char context_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::context>*); + +template +char (&on_work_started_memfn_helper(...))[2]; + +template +char on_work_started_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::on_work_started>*); + +template +char (&on_work_finished_memfn_helper(...))[2]; + +template +char on_work_finished_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::on_work_finished>*); + +template +char (&dispatch_memfn_helper(...))[2]; + +template +char dispatch_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::dispatch>*); + +template +char (&post_memfn_helper(...))[2]; + +template +char post_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::post>*); + +template +char (&defer_memfn_helper(...))[2]; + +template +char defer_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::defer>*); + +template +struct is_executor_class + : integral_constant(0)) != 1 && + sizeof(on_work_started_memfn_helper(0)) != 1 && + sizeof(on_work_finished_memfn_helper(0)) != 1 && + sizeof(dispatch_memfn_helper(0)) != 1 && + sizeof(post_memfn_helper(0)) != 1 && + sizeof(defer_memfn_helper(0)) != 1> +{ +}; + +template +struct is_executor + : conditional::value, + is_executor_class, + false_type>::type +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IS_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/detail/keyword_tss_ptr.hpp b/subprojects/asio/include/asio/detail/keyword_tss_ptr.hpp new file mode 100644 index 000000000..2ae651ab2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/keyword_tss_ptr.hpp @@ -0,0 +1,70 @@ +// +// detail/keyword_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_KEYWORD_TSS_PTR_HPP +#define ASIO_DETAIL_KEYWORD_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class keyword_tss_ptr + : private noncopyable +{ +public: + // Constructor. + keyword_tss_ptr() + { + } + + // Destructor. + ~keyword_tss_ptr() + { + } + + // Get the value. + operator T*() const + { + return value_; + } + + // Set the value. + void operator=(T* value) + { + value_ = value; + } + +private: + static ASIO_THREAD_KEYWORD T* value_; +}; + +template +ASIO_THREAD_KEYWORD T* keyword_tss_ptr::value_; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + +#endif // ASIO_DETAIL_KEYWORD_TSS_PTR_HPP diff --git a/subprojects/asio/include/asio/detail/kqueue_reactor.hpp b/subprojects/asio/include/asio/detail/kqueue_reactor.hpp new file mode 100644 index 000000000..43cb9f935 --- /dev/null +++ b/subprojects/asio/include/asio/detail/kqueue_reactor.hpp @@ -0,0 +1,242 @@ +// +// detail/kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include +#include +#include +#include +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/object_pool.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" + +// Older versions of Mac OS X may not define EV_OOBAND. +#if !defined(EV_OOBAND) +# define EV_OOBAND EV_FLAG1 +#endif // !defined(EV_OOBAND) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class scheduler; + +class kqueue_reactor + : public execution_context_service_base +{ +private: + // The mutex type used by this reactor. + typedef conditionally_enabled_mutex mutex; + +public: + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + struct descriptor_state + { + descriptor_state(bool locking) : mutex_(locking) {} + + friend class kqueue_reactor; + friend class object_pool_access; + + descriptor_state* next_; + descriptor_state* prev_; + + mutex mutex_; + int descriptor_; + int num_kevents_; // 1 == read only, 2 == read and write + op_queue op_queue_[max_ops]; + bool shutdown_; + }; + + // Per-descriptor data. + typedef descriptor_state* per_descriptor_data; + + // Constructor. + ASIO_DECL kqueue_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~kqueue_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data& descriptor_data); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data( + per_descriptor_data& descriptor_data); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run the kqueue loop. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the kqueue loop. + ASIO_DECL void interrupt(); + +private: + // Create the kqueue file descriptor. Throws an exception if the descriptor + // cannot be created. + ASIO_DECL static int do_kqueue_create(); + + // Allocate a new descriptor state object. + ASIO_DECL descriptor_state* allocate_descriptor_state(); + + // Free an existing descriptor state object. + ASIO_DECL void free_descriptor_state(descriptor_state* s); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Get the timeout value for the kevent call. + ASIO_DECL timespec* get_timeout(long usec, timespec& ts); + + // The scheduler used to post completions. + scheduler& scheduler_; + + // Mutex to protect access to internal data. + mutex mutex_; + + // The kqueue file descriptor. + int kqueue_fd_; + + // The interrupter is used to break a blocking kevent call. + select_interrupter interrupter_; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; + + // Mutex to protect access to the registered descriptors. + mutex registered_descriptors_mutex_; + + // Keep track of all registered descriptors. + object_pool registered_descriptors_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/kqueue_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/kqueue_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/limits.hpp b/subprojects/asio/include/asio/detail/limits.hpp new file mode 100644 index 000000000..d32470d2d --- /dev/null +++ b/subprojects/asio/include/asio/detail/limits.hpp @@ -0,0 +1,26 @@ +// +// detail/limits.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_LIMITS_HPP +#define ASIO_DETAIL_LIMITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_LIMITS) +# include +#else // defined(ASIO_HAS_BOOST_LIMITS) +# include +#endif // defined(ASIO_HAS_BOOST_LIMITS) + +#endif // ASIO_DETAIL_LIMITS_HPP diff --git a/subprojects/asio/include/asio/detail/local_free_on_block_exit.hpp b/subprojects/asio/include/asio/detail/local_free_on_block_exit.hpp new file mode 100644 index 000000000..eba6b7771 --- /dev/null +++ b/subprojects/asio/include/asio/detail/local_free_on_block_exit.hpp @@ -0,0 +1,59 @@ +// +// detail/local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +#if !defined(ASIO_WINDOWS_APP) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class local_free_on_block_exit + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + explicit local_free_on_block_exit(void* p) + : p_(p) + { + } + + // Destructor restores the previous signal mask. + ~local_free_on_block_exit() + { + ::LocalFree(p_); + } + +private: + void* p_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS_APP) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/subprojects/asio/include/asio/detail/macos_fenced_block.hpp b/subprojects/asio/include/asio/detail/macos_fenced_block.hpp new file mode 100644 index 000000000..bbac270a4 --- /dev/null +++ b/subprojects/asio/include/asio/detail/macos_fenced_block.hpp @@ -0,0 +1,62 @@ +// +// detail/macos_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP +#define ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__MACH__) && defined(__APPLE__) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class macos_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit macos_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit macos_fenced_block(full_t) + { + OSMemoryBarrier(); + } + + // Destructor. + ~macos_fenced_block() + { + OSMemoryBarrier(); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__MACH__) && defined(__APPLE__) + +#endif // ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/memory.hpp b/subprojects/asio/include/asio/detail/memory.hpp new file mode 100644 index 000000000..b1ec4975f --- /dev/null +++ b/subprojects/asio/include/asio/detail/memory.hpp @@ -0,0 +1,70 @@ +// +// detail/memory.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_MEMORY_HPP +#define ASIO_DETAIL_MEMORY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#if !defined(ASIO_HAS_STD_SHARED_PTR) +# include +# include +#endif // !defined(ASIO_HAS_STD_SHARED_PTR) + +#if !defined(ASIO_HAS_STD_ADDRESSOF) +# include +#endif // !defined(ASIO_HAS_STD_ADDRESSOF) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_SHARED_PTR) +using std::shared_ptr; +using std::weak_ptr; +#else // defined(ASIO_HAS_STD_SHARED_PTR) +using boost::shared_ptr; +using boost::weak_ptr; +#endif // defined(ASIO_HAS_STD_SHARED_PTR) + +#if defined(ASIO_HAS_STD_ADDRESSOF) +using std::addressof; +#else // defined(ASIO_HAS_STD_ADDRESSOF) +using boost::addressof; +#endif // defined(ASIO_HAS_STD_ADDRESSOF) + +} // namespace detail + +#if defined(ASIO_HAS_CXX11_ALLOCATORS) +using std::allocator_arg_t; +# define ASIO_USES_ALLOCATOR(t) \ + namespace std { \ + template \ + struct uses_allocator : true_type {}; \ + } \ + /**/ +# define ASIO_REBIND_ALLOC(alloc, t) \ + typename std::allocator_traits::template rebind_alloc + /**/ +#else // defined(ASIO_HAS_CXX11_ALLOCATORS) +struct allocator_arg_t {}; +# define ASIO_USES_ALLOCATOR(t) +# define ASIO_REBIND_ALLOC(alloc, t) \ + typename alloc::template rebind::other + /**/ +#endif // defined(ASIO_HAS_CXX11_ALLOCATORS) + +} // namespace asio + +#endif // ASIO_DETAIL_MEMORY_HPP diff --git a/subprojects/asio/include/asio/detail/mutex.hpp b/subprojects/asio/include/asio/detail/mutex.hpp new file mode 100644 index 000000000..2f8f0b132 --- /dev/null +++ b/subprojects/asio/include/asio/detail/mutex.hpp @@ -0,0 +1,48 @@ +// +// detail/mutex.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_MUTEX_HPP +#define ASIO_DETAIL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_mutex.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_mutex.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_mutex.hpp" +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# include "asio/detail/std_mutex.hpp" +#else +# error Only Windows, POSIX and std::mutex are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_mutex mutex; +#elif defined(ASIO_WINDOWS) +typedef win_mutex mutex; +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_mutex mutex; +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +typedef std_mutex mutex; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/noncopyable.hpp b/subprojects/asio/include/asio/detail/noncopyable.hpp new file mode 100644 index 000000000..0c038e11e --- /dev/null +++ b/subprojects/asio/include/asio/detail/noncopyable.hpp @@ -0,0 +1,43 @@ +// +// detail/noncopyable.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NONCOPYABLE_HPP +#define ASIO_DETAIL_NONCOPYABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class noncopyable +{ +protected: + noncopyable() {} + ~noncopyable() {} +private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); +}; + +} // namespace detail + +using asio::detail::noncopyable; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NONCOPYABLE_HPP diff --git a/subprojects/asio/include/asio/detail/null_event.hpp b/subprojects/asio/include/asio/detail/null_event.hpp new file mode 100644 index 000000000..5686a41e7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_event.hpp @@ -0,0 +1,100 @@ +// +// detail/null_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_EVENT_HPP +#define ASIO_DETAIL_NULL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_event + : private noncopyable +{ +public: + // Constructor. + null_event() + { + } + + // Destructor. + ~null_event() + { + } + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock&) + { + } + + // Signal all waiters. + template + void signal_all(Lock&) + { + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock&) + { + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock&) + { + return false; + } + + // Reset the event. + template + void clear(Lock&) + { + } + + // Wait for the event to become signalled. + template + void wait(Lock&) + { + do_wait(); + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock&, long usec) + { + do_wait_for_usec(usec); + return true; + } + +private: + ASIO_DECL static void do_wait(); + ASIO_DECL static void do_wait_for_usec(long usec); +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/null_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_NULL_EVENT_HPP diff --git a/subprojects/asio/include/asio/detail/null_fenced_block.hpp b/subprojects/asio/include/asio/detail/null_fenced_block.hpp new file mode 100644 index 000000000..0275326b1 --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_fenced_block.hpp @@ -0,0 +1,47 @@ +// +// detail/null_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_FENCED_BLOCK_HPP +#define ASIO_DETAIL_NULL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_fenced_block + : private noncopyable +{ +public: + enum half_or_full_t { half, full }; + + // Constructor. + explicit null_fenced_block(half_or_full_t) + { + } + + // Destructor. + ~null_fenced_block() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/null_global.hpp b/subprojects/asio/include/asio/detail/null_global.hpp new file mode 100644 index 000000000..727dd3f49 --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_global.hpp @@ -0,0 +1,59 @@ +// +// detail/null_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_GLOBAL_HPP +#define ASIO_DETAIL_NULL_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct null_global_impl +{ + null_global_impl() + : ptr_(0) + { + } + + // Destructor automatically cleans up the global. + ~null_global_impl() + { + delete ptr_; + } + + static null_global_impl instance_; + T* ptr_; +}; + +template +null_global_impl null_global_impl::instance_; + +template +T& null_global() +{ + if (null_global_impl::instance_.ptr_ == 0) + null_global_impl::instance_.ptr_ = new T; + return *null_global_impl::instance_.ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_GLOBAL_HPP diff --git a/subprojects/asio/include/asio/detail/null_mutex.hpp b/subprojects/asio/include/asio/detail/null_mutex.hpp new file mode 100644 index 000000000..afe3fc032 --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_mutex.hpp @@ -0,0 +1,64 @@ +// +// detail/null_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_MUTEX_HPP +#define ASIO_DETAIL_NULL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + null_mutex() + { + } + + // Destructor. + ~null_mutex() + { + } + + // Lock the mutex. + void lock() + { + } + + // Unlock the mutex. + void unlock() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/null_reactor.hpp b/subprojects/asio/include/asio/detail/null_reactor.hpp new file mode 100644 index 000000000..ca3c5fde6 --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_reactor.hpp @@ -0,0 +1,68 @@ +// +// detail/null_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_REACTOR_HPP +#define ASIO_DETAIL_NULL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/scheduler_operation.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_reactor + : public execution_context_service_base +{ +public: + // Constructor. + null_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx) + { + } + + // Destructor. + ~null_reactor() + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // No-op because should never be called. + void run(long /*usec*/, op_queue& /*ops*/) + { + } + + // No-op. + void interrupt() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_NULL_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/null_signal_blocker.hpp b/subprojects/asio/include/asio/detail/null_signal_blocker.hpp new file mode 100644 index 000000000..edfe82051 --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_signal_blocker.hpp @@ -0,0 +1,69 @@ +// +// detail/null_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) \ + || defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + null_signal_blocker() + { + } + + // Destructor restores the previous signal mask. + ~null_signal_blocker() + { + } + + // Block all signals for the calling thread. + void block() + { + } + + // Restore the previous signal mask. + void unblock() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + // || defined(ASIO_WINDOWS) + // || defined(ASIO_WINDOWS_RUNTIME) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + +#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP diff --git a/subprojects/asio/include/asio/detail/null_socket_service.hpp b/subprojects/asio/include/asio/detail/null_socket_service.hpp new file mode 100644 index 000000000..109c6c7bd --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_socket_service.hpp @@ -0,0 +1,508 @@ +// +// detail/null_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_NULL_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class null_socket_service : + public service_base > +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef int native_handle_type; + + // The implementation type of the socket. + struct implementation_type + { + }; + + // Constructor. + null_socket_service(asio::io_context& io_context) + : service_base >(io_context), + io_context_(io_context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // Construct a new socket implementation. + void construct(implementation_type&) + { + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type&, implementation_type&) + { + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type&, + null_socket_service&, implementation_type&) + { + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type&, + null_socket_service&, + typename null_socket_service::implementation_type&) + { + } + + // Destroy a socket implementation. + void destroy(implementation_type&) + { + } + + // Open a new socket implementation. + asio::error_code open(implementation_type&, + const protocol_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type&, const protocol_type&, + const native_handle_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Determine whether the socket is open. + bool is_open(const implementation_type&) const + { + return false; + } + + // Destroy a socket implementation. + asio::error_code close(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Release ownership of the socket. + native_handle_type release(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type&) + { + return 0; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return false; + } + + // Determine the number of bytes available for reading. + std::size_t available(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(implementation_type&, + int, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type&, + IO_Control_Command&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type&, + socket_base::shutdown_type, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type&, + const Option&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type&, + Option&, asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return endpoint_type(); + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return endpoint_type(); + } + + // Send the given data to the peer. + template + std::size_t send(implementation_type&, const ConstBufferSequence&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be sent without blocking. + std::size_t send(implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(implementation_type&, const ConstBufferSequence&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Receive some data from the peer. Returns the number of bytes received. + template + std::size_t receive(implementation_type&, const MutableBufferSequence&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be received without blocking. + std::size_t receive(implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(implementation_type&, const MutableBufferSequence&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Wait until data can be received without blocking. + template + void async_receive(implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template + std::size_t receive_with_flags(implementation_type&, + const MutableBufferSequence&, socket_base::message_flags, + socket_base::message_flags&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be received without blocking. + std::size_t receive_with_flags(implementation_type&, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive_with_flags(implementation_type&, + const MutableBufferSequence&, socket_base::message_flags, + socket_base::message_flags&, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Wait until data can be received without blocking. + template + void async_receive_with_flags(implementation_type&, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags&, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + std::size_t send_to(implementation_type&, const ConstBufferSequence&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be sent without blocking. + std::size_t send_to(implementation_type&, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type&, const ConstBufferSequence&, + const endpoint_type&, socket_base::message_flags, + Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type&, const null_buffers&, + const endpoint_type&, socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + std::size_t receive_from(implementation_type&, const MutableBufferSequence&, + endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be received without blocking. + std::size_t receive_from(implementation_type&, const null_buffers&, + endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type&, + const MutableBufferSequence&, endpoint_type&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type&, + const null_buffers&, endpoint_type&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.post(detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type&, + Socket&, endpoint_type*, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept(implementation_type&, Socket&, + endpoint_type*, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + io_context_.post(detail::bind_handler(handler, ec)); + } + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type&, + const endpoint_type&, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + io_context_.post(detail::bind_handler(handler, ec)); + } + +private: + asio::io_context& io_context_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_NULL_SOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/null_static_mutex.hpp b/subprojects/asio/include/asio/detail/null_static_mutex.hpp new file mode 100644 index 000000000..36ec04f7e --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_static_mutex.hpp @@ -0,0 +1,60 @@ +// +// detail/null_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_STATIC_MUTEX_HPP +#define ASIO_DETAIL_NULL_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct null_static_mutex +{ + typedef asio::detail::scoped_lock scoped_lock; + + // Initialise the mutex. + void init() + { + } + + // Lock the mutex. + void lock() + { + } + + // Unlock the mutex. + void unlock() + { + } + + int unused_; +}; + +#define ASIO_NULL_STATIC_MUTEX_INIT { 0 } + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_STATIC_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/null_thread.hpp b/subprojects/asio/include/asio/detail/null_thread.hpp new file mode 100644 index 000000000..7291ba384 --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_thread.hpp @@ -0,0 +1,67 @@ +// +// detail/null_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_THREAD_HPP +#define ASIO_DETAIL_NULL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_thread + : private noncopyable +{ +public: + // Constructor. + template + null_thread(Function, unsigned int = 0) + { + asio::detail::throw_error( + asio::error::operation_not_supported, "thread"); + } + + // Destructor. + ~null_thread() + { + } + + // Wait for the thread to exit. + void join() + { + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + return 1; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_THREAD_HPP diff --git a/subprojects/asio/include/asio/detail/null_tss_ptr.hpp b/subprojects/asio/include/asio/detail/null_tss_ptr.hpp new file mode 100644 index 000000000..323967d6c --- /dev/null +++ b/subprojects/asio/include/asio/detail/null_tss_ptr.hpp @@ -0,0 +1,68 @@ +// +// detail/null_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_TSS_PTR_HPP +#define ASIO_DETAIL_NULL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class null_tss_ptr + : private noncopyable +{ +public: + // Constructor. + null_tss_ptr() + : value_(0) + { + } + + // Destructor. + ~null_tss_ptr() + { + } + + // Get the value. + operator T*() const + { + return value_; + } + + // Set the value. + void operator=(T* value) + { + value_ = value; + } + +private: + T* value_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP diff --git a/subprojects/asio/include/asio/detail/object_pool.hpp b/subprojects/asio/include/asio/detail/object_pool.hpp new file mode 100644 index 000000000..e1a3c5482 --- /dev/null +++ b/subprojects/asio/include/asio/detail/object_pool.hpp @@ -0,0 +1,171 @@ +// +// detail/object_pool.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OBJECT_POOL_HPP +#define ASIO_DETAIL_OBJECT_POOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class object_pool; + +class object_pool_access +{ +public: + template + static Object* create() + { + return new Object; + } + + template + static Object* create(Arg arg) + { + return new Object(arg); + } + + template + static void destroy(Object* o) + { + delete o; + } + + template + static Object*& next(Object* o) + { + return o->next_; + } + + template + static Object*& prev(Object* o) + { + return o->prev_; + } +}; + +template +class object_pool + : private noncopyable +{ +public: + // Constructor. + object_pool() + : live_list_(0), + free_list_(0) + { + } + + // Destructor destroys all objects. + ~object_pool() + { + destroy_list(live_list_); + destroy_list(free_list_); + } + + // Get the object at the start of the live list. + Object* first() + { + return live_list_; + } + + // Allocate a new object. + Object* alloc() + { + Object* o = free_list_; + if (o) + free_list_ = object_pool_access::next(free_list_); + else + o = object_pool_access::create(); + + object_pool_access::next(o) = live_list_; + object_pool_access::prev(o) = 0; + if (live_list_) + object_pool_access::prev(live_list_) = o; + live_list_ = o; + + return o; + } + + // Allocate a new object with an argument. + template + Object* alloc(Arg arg) + { + Object* o = free_list_; + if (o) + free_list_ = object_pool_access::next(free_list_); + else + o = object_pool_access::create(arg); + + object_pool_access::next(o) = live_list_; + object_pool_access::prev(o) = 0; + if (live_list_) + object_pool_access::prev(live_list_) = o; + live_list_ = o; + + return o; + } + + // Free an object. Moves it to the free list. No destructors are run. + void free(Object* o) + { + if (live_list_ == o) + live_list_ = object_pool_access::next(o); + + if (object_pool_access::prev(o)) + { + object_pool_access::next(object_pool_access::prev(o)) + = object_pool_access::next(o); + } + + if (object_pool_access::next(o)) + { + object_pool_access::prev(object_pool_access::next(o)) + = object_pool_access::prev(o); + } + + object_pool_access::next(o) = free_list_; + object_pool_access::prev(o) = 0; + free_list_ = o; + } + +private: + // Helper function to destroy all elements in a list. + void destroy_list(Object* list) + { + while (list) + { + Object* o = list; + list = object_pool_access::next(o); + object_pool_access::destroy(o); + } + } + + // The list of live objects. + Object* live_list_; + + // The free list. + Object* free_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OBJECT_POOL_HPP diff --git a/subprojects/asio/include/asio/detail/old_win_sdk_compat.hpp b/subprojects/asio/include/asio/detail/old_win_sdk_compat.hpp new file mode 100644 index 000000000..bfb109e7f --- /dev/null +++ b/subprojects/asio/include/asio/detail/old_win_sdk_compat.hpp @@ -0,0 +1,214 @@ +// +// detail/old_win_sdk_compat.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP +#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Guess whether we are building against on old Platform SDK. +#if !defined(IN6ADDR_ANY_INIT) +#define ASIO_HAS_OLD_WIN_SDK 1 +#endif // !defined(IN6ADDR_ANY_INIT) + +#if defined(ASIO_HAS_OLD_WIN_SDK) + +// Emulation of types that are missing from old Platform SDKs. +// +// N.B. this emulation is also used if building for a Windows 2000 target with +// a recent (i.e. Vista or later) SDK, as the SDK does not provide IPv6 support +// in that case. + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +enum +{ + sockaddr_storage_maxsize = 128, // Maximum size. + sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment. + sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)), + sockaddr_storage_pad2size = (sockaddr_storage_maxsize - + (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize)) +}; + +struct sockaddr_storage_emulation +{ + short ss_family; + char __ss_pad1[sockaddr_storage_pad1size]; + __int64 __ss_align; + char __ss_pad2[sockaddr_storage_pad2size]; +}; + +struct in6_addr_emulation +{ + union + { + u_char Byte[16]; + u_short Word[8]; + } u; +}; + +#if !defined(s6_addr) +# define _S6_un u +# define _S6_u8 Byte +# define s6_addr _S6_un._S6_u8 +#endif // !defined(s6_addr) + +struct sockaddr_in6_emulation +{ + short sin6_family; + u_short sin6_port; + u_long sin6_flowinfo; + in6_addr_emulation sin6_addr; + u_long sin6_scope_id; +}; + +struct ipv6_mreq_emulation +{ + in6_addr_emulation ipv6mr_multiaddr; + unsigned int ipv6mr_interface; +}; + +struct addrinfo_emulation +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char* ai_canonname; + sockaddr* ai_addr; + addrinfo_emulation* ai_next; +}; + +#if !defined(AI_PASSIVE) +# define AI_PASSIVE 0x1 +#endif + +#if !defined(AI_CANONNAME) +# define AI_CANONNAME 0x2 +#endif + +#if !defined(AI_NUMERICHOST) +# define AI_NUMERICHOST 0x4 +#endif + +#if !defined(EAI_AGAIN) +# define EAI_AGAIN WSATRY_AGAIN +#endif + +#if !defined(EAI_BADFLAGS) +# define EAI_BADFLAGS WSAEINVAL +#endif + +#if !defined(EAI_FAIL) +# define EAI_FAIL WSANO_RECOVERY +#endif + +#if !defined(EAI_FAMILY) +# define EAI_FAMILY WSAEAFNOSUPPORT +#endif + +#if !defined(EAI_MEMORY) +# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#endif + +#if !defined(EAI_NODATA) +# define EAI_NODATA WSANO_DATA +#endif + +#if !defined(EAI_NONAME) +# define EAI_NONAME WSAHOST_NOT_FOUND +#endif + +#if !defined(EAI_SERVICE) +# define EAI_SERVICE WSATYPE_NOT_FOUND +#endif + +#if !defined(EAI_SOCKTYPE) +# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif + +#if !defined(NI_NOFQDN) +# define NI_NOFQDN 0x01 +#endif + +#if !defined(NI_NUMERICHOST) +# define NI_NUMERICHOST 0x02 +#endif + +#if !defined(NI_NAMEREQD) +# define NI_NAMEREQD 0x04 +#endif + +#if !defined(NI_NUMERICSERV) +# define NI_NUMERICSERV 0x08 +#endif + +#if !defined(NI_DGRAM) +# define NI_DGRAM 0x10 +#endif + +#if !defined(IPPROTO_IPV6) +# define IPPROTO_IPV6 41 +#endif + +#if !defined(IPV6_UNICAST_HOPS) +# define IPV6_UNICAST_HOPS 4 +#endif + +#if !defined(IPV6_MULTICAST_IF) +# define IPV6_MULTICAST_IF 9 +#endif + +#if !defined(IPV6_MULTICAST_HOPS) +# define IPV6_MULTICAST_HOPS 10 +#endif + +#if !defined(IPV6_MULTICAST_LOOP) +# define IPV6_MULTICAST_LOOP 11 +#endif + +#if !defined(IPV6_JOIN_GROUP) +# define IPV6_JOIN_GROUP 12 +#endif + +#if !defined(IPV6_LEAVE_GROUP) +# define IPV6_LEAVE_GROUP 13 +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_OLD_WIN_SDK) + +// Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY. +#if !defined(IPV6_V6ONLY) +# define IPV6_V6ONLY 27 +#endif + +// Some SDKs (e.g. Windows CE) don't define IPPROTO_ICMPV6. +#if !defined(IPPROTO_ICMPV6) +# define IPPROTO_ICMPV6 58 +#endif + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP diff --git a/subprojects/asio/include/asio/detail/op_queue.hpp b/subprojects/asio/include/asio/detail/op_queue.hpp new file mode 100644 index 000000000..6219f797d --- /dev/null +++ b/subprojects/asio/include/asio/detail/op_queue.hpp @@ -0,0 +1,162 @@ +// +// detail/op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OP_QUEUE_HPP +#define ASIO_DETAIL_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class op_queue; + +class op_queue_access +{ +public: + template + static Operation* next(Operation* o) + { + return static_cast(o->next_); + } + + template + static void next(Operation1*& o1, Operation2* o2) + { + o1->next_ = o2; + } + + template + static void destroy(Operation* o) + { + o->destroy(); + } + + template + static Operation*& front(op_queue& q) + { + return q.front_; + } + + template + static Operation*& back(op_queue& q) + { + return q.back_; + } +}; + +template +class op_queue + : private noncopyable +{ +public: + // Constructor. + op_queue() + : front_(0), + back_(0) + { + } + + // Destructor destroys all operations. + ~op_queue() + { + while (Operation* op = front_) + { + pop(); + op_queue_access::destroy(op); + } + } + + // Get the operation at the front of the queue. + Operation* front() + { + return front_; + } + + // Pop an operation from the front of the queue. + void pop() + { + if (front_) + { + Operation* tmp = front_; + front_ = op_queue_access::next(front_); + if (front_ == 0) + back_ = 0; + op_queue_access::next(tmp, static_cast(0)); + } + } + + // Push an operation on to the back of the queue. + void push(Operation* h) + { + op_queue_access::next(h, static_cast(0)); + if (back_) + { + op_queue_access::next(back_, h); + back_ = h; + } + else + { + front_ = back_ = h; + } + } + + // Push all operations from another queue on to the back of the queue. The + // source queue may contain operations of a derived type. + template + void push(op_queue& q) + { + if (Operation* other_front = op_queue_access::front(q)) + { + if (back_) + op_queue_access::next(back_, other_front); + else + front_ = other_front; + back_ = op_queue_access::back(q); + op_queue_access::front(q) = 0; + op_queue_access::back(q) = 0; + } + } + + // Whether the queue is empty. + bool empty() const + { + return front_ == 0; + } + + // Test whether an operation is already enqueued. + bool is_enqueued(Operation* o) const + { + return op_queue_access::next(o) != 0 || back_ == o; + } + +private: + friend class op_queue_access; + + // The front of the queue. + Operation* front_; + + // The back of the queue. + Operation* back_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OP_QUEUE_HPP diff --git a/subprojects/asio/include/asio/detail/operation.hpp b/subprojects/asio/include/asio/detail/operation.hpp new file mode 100644 index 000000000..811e54d4a --- /dev/null +++ b/subprojects/asio/include/asio/detail/operation.hpp @@ -0,0 +1,38 @@ +// +// detail/operation.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OPERATION_HPP +#define ASIO_DETAIL_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_operation.hpp" +#else +# include "asio/detail/scheduler_operation.hpp" +#endif + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) +typedef win_iocp_operation operation; +#else +typedef scheduler_operation operation; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_OPERATION_HPP diff --git a/subprojects/asio/include/asio/detail/pipe_select_interrupter.hpp b/subprojects/asio/include/asio/detail/pipe_select_interrupter.hpp new file mode 100644 index 000000000..55d7db491 --- /dev/null +++ b/subprojects/asio/include/asio/detail/pipe_select_interrupter.hpp @@ -0,0 +1,89 @@ +// +// detail/pipe_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) +#if !defined(ASIO_WINDOWS_RUNTIME) +#if !defined(__CYGWIN__) +#if !defined(__SYMBIAN32__) +#if !defined(ASIO_HAS_EVENTFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class pipe_select_interrupter +{ +public: + // Constructor. + ASIO_DECL pipe_select_interrupter(); + + // Destructor. + ASIO_DECL ~pipe_select_interrupter(); + + // Recreate the interrupter's descriptors. Used after a fork. + ASIO_DECL void recreate(); + + // Interrupt the select call. + ASIO_DECL void interrupt(); + + // Reset the select interrupt. Returns true if the call was interrupted. + ASIO_DECL bool reset(); + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // Open the descriptors. Throws on error. + ASIO_DECL void open_descriptors(); + + // Close the descriptors. + ASIO_DECL void close_descriptors(); + + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/pipe_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_HAS_EVENTFD) +#endif // !defined(__SYMBIAN32__) +#endif // !defined(__CYGWIN__) +#endif // !defined(ASIO_WINDOWS_RUNTIME) +#endif // !defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP diff --git a/subprojects/asio/include/asio/detail/pop_options.hpp b/subprojects/asio/include/asio/detail/pop_options.hpp new file mode 100644 index 000000000..1045612bf --- /dev/null +++ b/subprojects/asio/include/asio/detail/pop_options.hpp @@ -0,0 +1,135 @@ +// +// detail/pop_options.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility pop +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +#elif defined(__clang__) + +// Clang + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if defined(ASIO_OBJC_WORKAROUND) +# undef Protocol +# undef id +# undef ASIO_OBJC_WORKAROUND +# endif +# endif +# endif + +# if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) +# pragma GCC visibility pop +# endif // !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (pop) +# endif + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if defined(ASIO_OBJC_WORKAROUND) +# undef Protocol +# undef id +# undef ASIO_OBJC_WORKAROUND +# endif +# endif +# endif + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility pop +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +# if (__GNUC__ >= 7) +# pragma GCC diagnostic pop +# endif // (__GNUC__ >= 7) + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option pop +# pragma nopushoptwarn +# pragma nopackwarning + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (pop) +# pragma pack (pop) + +# if defined(__cplusplus_cli) || defined(__cplusplus_winrt) +# if defined(ASIO_CLR_WORKAROUND) +# undef generic +# undef ASIO_CLR_WORKAROUND +# endif +# endif + +#endif diff --git a/subprojects/asio/include/asio/detail/posix_event.hpp b/subprojects/asio/include/asio/detail/posix_event.hpp new file mode 100644 index 000000000..121065efb --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_event.hpp @@ -0,0 +1,162 @@ +// +// detail/posix_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_EVENT_HPP +#define ASIO_DETAIL_POSIX_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class posix_event + : private noncopyable +{ +public: + // Constructor. + ASIO_DECL posix_event(); + + // Destructor. + ~posix_event() + { + ::pthread_cond_destroy(&cond_); + } + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock& lock) + { + this->signal_all(lock); + } + + // Signal all waiters. + template + void signal_all(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ |= 1; + ::pthread_cond_broadcast(&cond_); // Ignore EINVAL. + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + lock.unlock(); + if (have_waiters) + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + if (state_ > 1) + { + lock.unlock(); + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + return true; + } + return false; + } + + // Reset the event. + template + void clear(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ &= ~std::size_t(1); + } + + // Wait for the event to become signalled. + template + void wait(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + while ((state_ & 1) == 0) + { + state_ += 2; + ::pthread_cond_wait(&cond_, &lock.mutex().mutex_); // Ignore EINVAL. + state_ -= 2; + } + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock& lock, long usec) + { + ASIO_ASSERT(lock.locked()); + if ((state_ & 1) == 0) + { + state_ += 2; + timespec ts; +#if (defined(__MACH__) && defined(__APPLE__)) \ + || (defined(__ANDROID__) && (__ANDROID_API__ < 21) \ + && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + ::pthread_cond_timedwait_relative_np( + &cond_, &lock.mutex().mutex_, &ts); // Ignore EINVAL. +#else // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21) + // && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) + if (::clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + ts.tv_sec += usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + ts.tv_sec += ts.tv_nsec / 1000000000; + ts.tv_nsec = ts.tv_nsec % 1000000000; + ::pthread_cond_timedwait(&cond_, + &lock.mutex().mutex_, &ts); // Ignore EINVAL. + } +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21) + // && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) + state_ -= 2; + } + return (state_ & 1) != 0; + } + +private: + ::pthread_cond_t cond_; + std::size_t state_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_EVENT_HPP diff --git a/subprojects/asio/include/asio/detail/posix_fd_set_adapter.hpp b/subprojects/asio/include/asio/detail/posix_fd_set_adapter.hpp new file mode 100644 index 000000000..95042d663 --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_fd_set_adapter.hpp @@ -0,0 +1,118 @@ +// +// detail/posix_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(__CYGWIN__) \ + && !defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class posix_fd_set_adapter : noncopyable +{ +public: + posix_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + using namespace std; // Needed for memset on Solaris. + FD_ZERO(&fd_set_); + } + + void reset() + { + using namespace std; // Needed for memset on Solaris. + FD_ZERO(&fd_set_); + } + + bool set(socket_type descriptor) + { + if (descriptor < (socket_type)FD_SETSIZE) + { + if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) + max_descriptor_ = descriptor; + FD_SET(descriptor, &fd_set_); + return true; + } + return false; + } + + void set(reactor_op_queue& operations, op_queue& ops) + { + reactor_op_queue::iterator i = operations.begin(); + while (i != operations.end()) + { + reactor_op_queue::iterator op_iter = i++; + if (!set(op_iter->first)) + { + asio::error_code ec(error::fd_set_failure); + operations.cancel_operations(op_iter, ops, ec); + } + } + } + + bool is_set(socket_type descriptor) const + { + return FD_ISSET(descriptor, &fd_set_) != 0; + } + + operator fd_set*() + { + return &fd_set_; + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + + void perform(reactor_op_queue& operations, + op_queue& ops) const + { + reactor_op_queue::iterator i = operations.begin(); + while (i != operations.end()) + { + reactor_op_queue::iterator op_iter = i++; + if (is_set(op_iter->first)) + operations.perform_operations(op_iter, ops); + } + } + +private: + mutable fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) + // && !defined(__CYGWIN__) + // && !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP diff --git a/subprojects/asio/include/asio/detail/posix_global.hpp b/subprojects/asio/include/asio/detail/posix_global.hpp new file mode 100644 index 000000000..7ee0a7154 --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_global.hpp @@ -0,0 +1,80 @@ +// +// detail/posix_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_GLOBAL_HPP +#define ASIO_DETAIL_POSIX_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct posix_global_impl +{ + // Helper function to perform initialisation. + static void do_init() + { + instance_.static_ptr_ = instance_.ptr_ = new T; + } + + // Destructor automatically cleans up the global. + ~posix_global_impl() + { + delete static_ptr_; + } + + static ::pthread_once_t init_once_; + static T* static_ptr_; + static posix_global_impl instance_; + T* ptr_; +}; + +template +::pthread_once_t posix_global_impl::init_once_ = PTHREAD_ONCE_INIT; + +template +T* posix_global_impl::static_ptr_ = 0; + +template +posix_global_impl posix_global_impl::instance_; + +template +T& posix_global() +{ + int result = ::pthread_once( + &posix_global_impl::init_once_, + &posix_global_impl::do_init); + + if (result != 0) + std::terminate(); + + return *posix_global_impl::instance_.ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_GLOBAL_HPP diff --git a/subprojects/asio/include/asio/detail/posix_mutex.hpp b/subprojects/asio/include/asio/detail/posix_mutex.hpp new file mode 100644 index 000000000..c0d9fc9b7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_mutex.hpp @@ -0,0 +1,76 @@ +// +// detail/posix_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_MUTEX_HPP +#define ASIO_DETAIL_POSIX_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class posix_event; + +class posix_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + ASIO_DECL posix_mutex(); + + // Destructor. + ~posix_mutex() + { + ::pthread_mutex_destroy(&mutex_); // Ignore EBUSY. + } + + // Lock the mutex. + void lock() + { + (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. + } + + // Unlock the mutex. + void unlock() + { + (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. + } + +private: + friend class posix_event; + ::pthread_mutex_t mutex_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/posix_signal_blocker.hpp b/subprojects/asio/include/asio/detail/posix_signal_blocker.hpp new file mode 100644 index 000000000..fab5eb129 --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_signal_blocker.hpp @@ -0,0 +1,85 @@ +// +// detail/posix_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class posix_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + posix_signal_blocker() + : blocked_(false) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + + // Destructor restores the previous signal mask. + ~posix_signal_blocker() + { + if (blocked_) + pthread_sigmask(SIG_SETMASK, &old_mask_, 0); + } + + // Block all signals for the calling thread. + void block() + { + if (!blocked_) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + } + + // Restore the previous signal mask. + void unblock() + { + if (blocked_) + blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0); + } + +private: + // Have signals been blocked. + bool blocked_; + + // The previous signal mask. + sigset_t old_mask_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP diff --git a/subprojects/asio/include/asio/detail/posix_static_mutex.hpp b/subprojects/asio/include/asio/detail/posix_static_mutex.hpp new file mode 100644 index 000000000..59b86c188 --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_static_mutex.hpp @@ -0,0 +1,64 @@ +// +// detail/posix_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_STATIC_MUTEX_HPP +#define ASIO_DETAIL_POSIX_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct posix_static_mutex +{ + typedef asio::detail::scoped_lock scoped_lock; + + // Initialise the mutex. + void init() + { + // Nothing to do. + } + + // Lock the mutex. + void lock() + { + (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. + } + + // Unlock the mutex. + void unlock() + { + (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. + } + + ::pthread_mutex_t mutex_; +}; + +#define ASIO_POSIX_STATIC_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER } + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_STATIC_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/posix_thread.hpp b/subprojects/asio/include/asio/detail/posix_thread.hpp new file mode 100644 index 000000000..1817af28d --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_thread.hpp @@ -0,0 +1,109 @@ +// +// detail/posix_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_THREAD_HPP +#define ASIO_DETAIL_POSIX_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +extern "C" +{ + ASIO_DECL void* asio_detail_posix_thread_function(void* arg); +} + +class posix_thread + : private noncopyable +{ +public: + // Constructor. + template + posix_thread(Function f, unsigned int = 0) + : joined_(false) + { + start_thread(new func(f)); + } + + // Destructor. + ASIO_DECL ~posix_thread(); + + // Wait for the thread to exit. + ASIO_DECL void join(); + + // Get number of CPUs. + ASIO_DECL static std::size_t hardware_concurrency(); + +private: + friend void* asio_detail_posix_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + struct auto_func_base_ptr + { + func_base* ptr; + ~auto_func_base_ptr() { delete ptr; } + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ASIO_DECL void start_thread(func_base* arg); + + ::pthread_t thread_; + bool joined_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_thread.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_THREAD_HPP diff --git a/subprojects/asio/include/asio/detail/posix_tss_ptr.hpp b/subprojects/asio/include/asio/detail/posix_tss_ptr.hpp new file mode 100644 index 000000000..a3096b43e --- /dev/null +++ b/subprojects/asio/include/asio/detail/posix_tss_ptr.hpp @@ -0,0 +1,79 @@ +// +// detail/posix_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_TSS_PTR_HPP +#define ASIO_DETAIL_POSIX_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper function to create thread-specific storage. +ASIO_DECL void posix_tss_ptr_create(pthread_key_t& key); + +template +class posix_tss_ptr + : private noncopyable +{ +public: + // Constructor. + posix_tss_ptr() + { + posix_tss_ptr_create(tss_key_); + } + + // Destructor. + ~posix_tss_ptr() + { + ::pthread_key_delete(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::pthread_getspecific(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::pthread_setspecific(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + pthread_key_t tss_key_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_tss_ptr.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP diff --git a/subprojects/asio/include/asio/detail/push_options.hpp b/subprojects/asio/include/asio/detail/push_options.hpp new file mode 100644 index 000000000..0a3e97980 --- /dev/null +++ b/subprojects/asio/include/asio/detail/push_options.hpp @@ -0,0 +1,175 @@ +// +// detail/push_options.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility push (default) +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +#elif defined(__clang__) + +// Clang + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if !defined(ASIO_DISABLE_OBJC_WORKAROUND) +# if !defined(Protocol) && !defined(id) +# define Protocol cpp_Protocol +# define id cpp_id +# define ASIO_OBJC_WORKAROUND +# endif +# endif +# endif +# endif + +# if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) +# pragma GCC visibility push (default) +# endif // !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (push, 8) +# endif + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if !defined(ASIO_DISABLE_OBJC_WORKAROUND) +# if !defined(Protocol) && !defined(id) +# define Protocol cpp_Protocol +# define id cpp_id +# define ASIO_OBJC_WORKAROUND +# endif +# endif +# endif +# endif + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility push (default) +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +# if (__GNUC__ >= 7) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# endif // (__GNUC__ >= 7) + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi- +# pragma nopushoptwarn +# pragma nopackwarning +# if !defined(__MT__) +# error Multithreaded RTL must be selected. +# endif // !defined(__MT__) + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (disable:4103) +# pragma warning (push) +# pragma warning (disable:4127) +# pragma warning (disable:4180) +# pragma warning (disable:4244) +# pragma warning (disable:4355) +# pragma warning (disable:4510) +# pragma warning (disable:4512) +# pragma warning (disable:4610) +# pragma warning (disable:4675) +# if (_MSC_VER < 1600) +// Visual Studio 2008 generates spurious warnings about unused parameters. +# pragma warning (disable:4100) +# endif // (_MSC_VER < 1600) +# if defined(_M_IX86) && defined(_Wp64) +// The /Wp64 option is broken. If you want to check 64 bit portability, use a +// 64 bit compiler! +# pragma warning (disable:4311) +# pragma warning (disable:4312) +# endif // defined(_M_IX86) && defined(_Wp64) +# pragma pack (push, 8) +// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler +// has a tendency to incorrectly optimise away some calls to member template +// functions, even though those functions contain code that should not be +// optimised away! Therefore we will always disable this optimisation option +// for the MSVC6 compiler. +# if (_MSC_VER < 1300) +# pragma optimize ("g", off) +# endif +# if !defined(_MT) +# error Multithreaded RTL must be selected. +# endif // !defined(_MT) + +# if defined(__cplusplus_cli) || defined(__cplusplus_winrt) +# if !defined(ASIO_DISABLE_CLR_WORKAROUND) +# if !defined(generic) +# define generic cpp_generic +# define ASIO_CLR_WORKAROUND +# endif +# endif +# endif + +#endif diff --git a/subprojects/asio/include/asio/detail/reactive_descriptor_service.hpp b/subprojects/asio/include/asio/detail/reactive_descriptor_service.hpp new file mode 100644 index 000000000..e86686399 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_descriptor_service.hpp @@ -0,0 +1,388 @@ +// +// detail/reactive_descriptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include "asio/buffer.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/descriptor_read_op.hpp" +#include "asio/detail/descriptor_write_op.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_wait_op.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/posix/descriptor_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_descriptor_service : + public service_base +{ +public: + // The native type of a descriptor. + typedef int native_handle_type; + + // The implementation type of the descriptor. + class implementation_type + : private asio::detail::noncopyable + { + public: + // Default constructor. + implementation_type() + : descriptor_(-1), + state_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class reactive_descriptor_service; + + // The native descriptor representation. + int descriptor_; + + // The current state of the descriptor. + descriptor_ops::state_type state_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + ASIO_DECL reactive_descriptor_service( + asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new descriptor implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Move-construct a new descriptor implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl); + + // Move-assign from another descriptor implementation. + ASIO_DECL void move_assign(implementation_type& impl, + reactive_descriptor_service& other_service, + implementation_type& other_impl); + + // Destroy a descriptor implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Assign a native descriptor to a descriptor implementation. + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_handle_type& native_descriptor, + asio::error_code& ec); + + // Determine whether the descriptor is open. + bool is_open(const implementation_type& impl) const + { + return impl.descriptor_ != -1; + } + + // Destroy a descriptor implementation. + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); + + // Get the native descriptor representation. + native_handle_type native_handle(const implementation_type& impl) const + { + return impl.descriptor_; + } + + // Release ownership of the native descriptor representation. + ASIO_DECL native_handle_type release(implementation_type& impl); + + // Cancel all operations associated with the descriptor. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Perform an IO control command on the descriptor. + template + asio::error_code io_control(implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + descriptor_ops::ioctl(impl.descriptor_, impl.state_, + command.name(), static_cast(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the descriptor. + bool non_blocking(const implementation_type& impl) const + { + return (impl.state_ & descriptor_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the descriptor. + asio::error_code non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + descriptor_ops::set_user_non_blocking( + impl.descriptor_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native descriptor implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return (impl.state_ & descriptor_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native descriptor implementation. + asio::error_code native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + descriptor_ops::set_internal_non_blocking( + impl.descriptor_, impl.state_, mode, ec); + return ec; + } + + // Wait for the descriptor to become ready to read, ready to write, or to have + // pending error conditions. + asio::error_code wait(implementation_type& impl, + posix::descriptor_base::wait_type w, asio::error_code& ec) + { + switch (w) + { + case posix::descriptor_base::wait_read: + descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec); + break; + case posix::descriptor_base::wait_write: + descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec); + break; + case posix::descriptor_base::wait_error: + descriptor_ops::poll_error(impl.descriptor_, impl.state_, ec); + break; + default: + ec = asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the descriptor to become ready to read, ready to + // write, or to have pending error conditions. + template + void async_wait(implementation_type& impl, + posix::descriptor_base::wait_type w, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_wait_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_wait")); + + int op_type; + switch (w) + { + case posix::descriptor_base::wait_read: + op_type = reactor::read_op; + break; + case posix::descriptor_base::wait_write: + op_type = reactor::write_op; + break; + case posix::descriptor_base::wait_error: + op_type = reactor::except_op; + break; + default: + p.p->ec_ = asio::error::invalid_argument; + reactor_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + return; + } + + start_op(impl, op_type, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Write some data to the descriptor. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return descriptor_ops::sync_write(impl.descriptor_, impl.state_, + bufs.buffers(), bufs.count(), bufs.all_empty(), ec); + } + + // Wait until data can be written without blocking. + size_t write_some(implementation_type& impl, + const null_buffers&, asio::error_code& ec) + { + // Wait for descriptor to become ready. + descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec); + + return 0; + } + + // Start an asynchronous write. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef descriptor_write_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.descriptor_, buffers, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_write_some")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, + buffer_sequence_adapter::all_empty(buffers)); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be written without blocking. + template + void async_write_some(implementation_type& impl, + const null_buffers&, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_write_some(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Read some data from the stream. Returns the number of bytes read. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return descriptor_ops::sync_read(impl.descriptor_, impl.state_, + bufs.buffers(), bufs.count(), bufs.all_empty(), ec); + } + + // Wait until data can be read without blocking. + size_t read_some(implementation_type& impl, + const null_buffers&, asio::error_code& ec) + { + // Wait for descriptor to become ready. + descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec); + + return 0; + } + + // Start an asynchronous read. The buffer for the data being read must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef descriptor_read_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.descriptor_, buffers, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_read_some")); + + start_op(impl, reactor::read_op, p.p, is_continuation, true, + buffer_sequence_adapter::all_empty(buffers)); + p.v = p.p = 0; + } + + // Wait until data can be read without blocking. + template + void async_read_some(implementation_type& impl, + const null_buffers&, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_read_some(null_buffers)")); + + start_op(impl, reactor::read_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + +private: + // Start the asynchronous operation. + ASIO_DECL void start_op(implementation_type& impl, int op_type, + reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop); + + // The selector that performs event demultiplexing for the service. + reactor& reactor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_descriptor_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_null_buffers_op.hpp b/subprojects/asio/include/asio/detail/reactive_null_buffers_op.hpp new file mode 100644 index 000000000..740826928 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_null_buffers_op.hpp @@ -0,0 +1,90 @@ +// +// detail/reactive_null_buffers_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP +#define ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_null_buffers_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_null_buffers_op); + + reactive_null_buffers_op(Handler& handler) + : reactor_op(&reactive_null_buffers_op::do_perform, + &reactive_null_buffers_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_null_buffers_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_serial_port_service.hpp b/subprojects/asio/include/asio/detail/reactive_serial_port_service.hpp new file mode 100644 index 000000000..6fddd9f1a --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_serial_port_service.hpp @@ -0,0 +1,236 @@ +// +// detail/reactive_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Extend reactive_descriptor_service to provide serial port support. +class reactive_serial_port_service : + public service_base +{ +public: + // The native type of a serial port. + typedef reactive_descriptor_service::native_handle_type native_handle_type; + + // The implementation type of the serial port. + typedef reactive_descriptor_service::implementation_type implementation_type; + + ASIO_DECL reactive_serial_port_service( + asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new serial port implementation. + void construct(implementation_type& impl) + { + descriptor_service_.construct(impl); + } + + // Move-construct a new serial port implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + descriptor_service_.move_construct(impl, other_impl); + } + + // Move-assign from another serial port implementation. + void move_assign(implementation_type& impl, + reactive_serial_port_service& other_service, + implementation_type& other_impl) + { + descriptor_service_.move_assign(impl, + other_service.descriptor_service_, other_impl); + } + + // Destroy a serial port implementation. + void destroy(implementation_type& impl) + { + descriptor_service_.destroy(impl); + } + + // Open the serial port using the specified device name. + ASIO_DECL asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec); + + // Assign a native descriptor to a serial port implementation. + asio::error_code assign(implementation_type& impl, + const native_handle_type& native_descriptor, + asio::error_code& ec) + { + return descriptor_service_.assign(impl, native_descriptor, ec); + } + + // Determine whether the serial port is open. + bool is_open(const implementation_type& impl) const + { + return descriptor_service_.is_open(impl); + } + + // Destroy a serial port implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return descriptor_service_.close(impl, ec); + } + + // Get the native serial port representation. + native_handle_type native_handle(implementation_type& impl) + { + return descriptor_service_.native_handle(impl); + } + + // Cancel all operations associated with the serial port. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return descriptor_service_.cancel(impl, ec); + } + + // Set an option on the serial port. + template + asio::error_code set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + return do_set_option(impl, + &reactive_serial_port_service::store_option, + &option, ec); + } + + // Get an option from the serial port. + template + asio::error_code get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + return do_get_option(impl, + &reactive_serial_port_service::load_option, + &option, ec); + } + + // Send a break sequence to the serial port. + asio::error_code send_break(implementation_type& impl, + asio::error_code& ec) + { + errno = 0; + descriptor_ops::error_wrapper(::tcsendbreak( + descriptor_service_.native_handle(impl), 0), ec); + return ec; + } + + // Write the given data. Returns the number of bytes sent. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return descriptor_service_.write_some(impl, buffers, ec); + } + + // Start an asynchronous write. The data being written must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, Handler& handler) + { + descriptor_service_.async_write_some(impl, buffers, handler); + } + + // Read some data. Returns the number of bytes received. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return descriptor_service_.read_some(impl, buffers, ec); + } + + // Start an asynchronous read. The buffer for the data being received must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler& handler) + { + descriptor_service_.async_read_some(impl, buffers, handler); + } + +private: + // Function pointer type for storing a serial port option. + typedef asio::error_code (*store_function_type)( + const void*, termios&, asio::error_code&); + + // Helper function template to store a serial port option. + template + static asio::error_code store_option(const void* option, + termios& storage, asio::error_code& ec) + { + static_cast(option)->store(storage, ec); + return ec; + } + + // Helper function to set a serial port option. + ASIO_DECL asio::error_code do_set_option( + implementation_type& impl, store_function_type store, + const void* option, asio::error_code& ec); + + // Function pointer type for loading a serial port option. + typedef asio::error_code (*load_function_type)( + void*, const termios&, asio::error_code&); + + // Helper function template to load a serial port option. + template + static asio::error_code load_option(void* option, + const termios& storage, asio::error_code& ec) + { + static_cast(option)->load(storage, ec); + return ec; + } + + // Helper function to get a serial port option. + ASIO_DECL asio::error_code do_get_option( + const implementation_type& impl, load_function_type load, + void* option, asio::error_code& ec) const; + + // The implementation used for initiating asynchronous operations. + reactive_descriptor_service descriptor_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_serial_port_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) +#endif // defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_accept_op.hpp b/subprojects/asio/include/asio/detail/reactive_socket_accept_op.hpp new file mode 100644 index 000000000..4a98f047f --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_accept_op.hpp @@ -0,0 +1,217 @@ +// +// detail/reactive_socket_accept_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_accept_op_base : public reactor_op +{ +public: + reactive_socket_accept_op_base(socket_type socket, + socket_ops::state_type state, Socket& peer, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, func_type complete_func) + : reactor_op(&reactive_socket_accept_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + addrlen_(peer_endpoint ? peer_endpoint->capacity() : 0) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_accept_op_base* o( + static_cast(base)); + + socket_type new_socket = invalid_socket; + status result = socket_ops::non_blocking_accept(o->socket_, + o->state_, o->peer_endpoint_ ? o->peer_endpoint_->data() : 0, + o->peer_endpoint_ ? &o->addrlen_ : 0, o->ec_, new_socket) + ? done : not_done; + o->new_socket_.reset(new_socket); + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_accept", o->ec_)); + + return result; + } + + void do_assign() + { + if (new_socket_.get() != invalid_socket) + { + if (peer_endpoint_) + peer_endpoint_->resize(addrlen_); + peer_.assign(protocol_, new_socket_.get(), ec_); + if (!ec_) + new_socket_.release(); + } + } + +private: + socket_type socket_; + socket_ops::state_type state_; + socket_holder new_socket_; + Socket& peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; + std::size_t addrlen_; +}; + +template +class reactive_socket_accept_op : + public reactive_socket_accept_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_accept_op); + + reactive_socket_accept_op(socket_type socket, + socket_ops::state_type state, Socket& peer, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, Handler& handler) + : reactive_socket_accept_op_base(socket, state, peer, + protocol, peer_endpoint, &reactive_socket_accept_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_accept_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + // On success, assign new connection to peer socket object. + if (owner) + o->do_assign(); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +#if defined(ASIO_HAS_MOVE) + +template +class reactive_socket_move_accept_op : + private Protocol::socket, + public reactive_socket_accept_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_move_accept_op); + + reactive_socket_move_accept_op(io_context& ioc, socket_type socket, + socket_ops::state_type state, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, Handler& handler) + : Protocol::socket(ioc), + reactive_socket_accept_op_base( + socket, state, *this, protocol, peer_endpoint, + &reactive_socket_move_accept_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_move_accept_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + // On success, assign new connection to peer socket object. + if (owner) + o->do_assign(); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::move_binder2 + handler(0, ASIO_MOVE_CAST(Handler)(o->handler_), o->ec_, + ASIO_MOVE_CAST(typename Protocol::socket)(*o)); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_connect_op.hpp b/subprojects/asio/include/asio/detail/reactive_socket_connect_op.hpp new file mode 100644 index 000000000..76164b2a8 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_connect_op.hpp @@ -0,0 +1,113 @@ +// +// detail/reactive_socket_connect_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_socket_connect_op_base : public reactor_op +{ +public: + reactive_socket_connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(&reactive_socket_connect_op_base::do_perform, complete_func), + socket_(socket) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_connect_op_base* o( + static_cast(base)); + + status result = socket_ops::non_blocking_connect( + o->socket_, o->ec_) ? done : not_done; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_connect", o->ec_)); + + return result; + } + +private: + socket_type socket_; +}; + +template +class reactive_socket_connect_op : public reactive_socket_connect_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_connect_op); + + reactive_socket_connect_op(socket_type socket, Handler& handler) + : reactive_socket_connect_op_base(socket, + &reactive_socket_connect_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_connect_op* o + (static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_recv_op.hpp b/subprojects/asio/include/asio/detail/reactive_socket_recv_op.hpp new file mode 100644 index 000000000..04bd266f9 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_recv_op.hpp @@ -0,0 +1,135 @@ +// +// detail/reactive_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recv_op_base : public reactor_op +{ +public: + reactive_socket_recv_op_base(socket_type socket, + socket_ops::state_type state, const MutableBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_recv_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + buffers_(buffers), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recv_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + status result = socket_ops::non_blocking_recv(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + (o->state_ & socket_ops::stream_oriented) != 0, + o->ec_, o->bytes_transferred_) ? done : not_done; + + if (result == done) + if ((o->state_ & socket_ops::stream_oriented) != 0) + if (o->bytes_transferred_ == 0) + result = done_and_exhausted; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recv", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + socket_ops::state_type state_; + MutableBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_recv_op : + public reactive_socket_recv_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recv_op); + + reactive_socket_recv_op(socket_type socket, + socket_ops::state_type state, const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + : reactive_socket_recv_op_base(socket, state, + buffers, flags, &reactive_socket_recv_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recv_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_recvfrom_op.hpp b/subprojects/asio/include/asio/detail/reactive_socket_recvfrom_op.hpp new file mode 100644 index 000000000..3d4fa4cad --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_recvfrom_op.hpp @@ -0,0 +1,138 @@ +// +// detail/reactive_socket_recvfrom_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recvfrom_op_base : public reactor_op +{ +public: + reactive_socket_recvfrom_op_base(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_recvfrom_op_base::do_perform, complete_func), + socket_(socket), + protocol_type_(protocol_type), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvfrom_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + std::size_t addr_len = o->sender_endpoint_.capacity(); + status result = socket_ops::non_blocking_recvfrom(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->sender_endpoint_.data(), &addr_len, + o->ec_, o->bytes_transferred_) ? done : not_done; + + if (result && !o->ec_) + o->sender_endpoint_.resize(addr_len); + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvfrom", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + int protocol_type_; + MutableBufferSequence buffers_; + Endpoint& sender_endpoint_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_recvfrom_op : + public reactive_socket_recvfrom_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvfrom_op); + + reactive_socket_recvfrom_op(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, Handler& handler) + : reactive_socket_recvfrom_op_base( + socket, protocol_type, buffers, endpoint, flags, + &reactive_socket_recvfrom_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvfrom_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_recvmsg_op.hpp b/subprojects/asio/include/asio/detail/reactive_socket_recvmsg_op.hpp new file mode 100644 index 000000000..f47327472 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_recvmsg_op.hpp @@ -0,0 +1,132 @@ +// +// detail/reactive_socket_recvmsg_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/socket_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recvmsg_op_base : public reactor_op +{ +public: + reactive_socket_recvmsg_op_base(socket_type socket, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, func_type complete_func) + : reactor_op(&reactive_socket_recvmsg_op_base::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + in_flags_(in_flags), + out_flags_(out_flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvmsg_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + status result = socket_ops::non_blocking_recvmsg(o->socket_, + bufs.buffers(), bufs.count(), + o->in_flags_, o->out_flags_, + o->ec_, o->bytes_transferred_) ? done : not_done; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvmsg", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + MutableBufferSequence buffers_; + socket_base::message_flags in_flags_; + socket_base::message_flags& out_flags_; +}; + +template +class reactive_socket_recvmsg_op : + public reactive_socket_recvmsg_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvmsg_op); + + reactive_socket_recvmsg_op(socket_type socket, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler) + : reactive_socket_recvmsg_op_base(socket, buffers, + in_flags, out_flags, &reactive_socket_recvmsg_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvmsg_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_send_op.hpp b/subprojects/asio/include/asio/detail/reactive_socket_send_op.hpp new file mode 100644 index 000000000..bd550d9c5 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_send_op.hpp @@ -0,0 +1,134 @@ +// +// detail/reactive_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_send_op_base : public reactor_op +{ +public: + reactive_socket_send_op_base(socket_type socket, + socket_ops::state_type state, const ConstBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_send_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + buffers_(buffers), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_send_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + status result = socket_ops::non_blocking_send(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->ec_, o->bytes_transferred_) ? done : not_done; + + if (result == done) + if ((o->state_ & socket_ops::stream_oriented) != 0) + if (o->bytes_transferred_ < bufs.total_size()) + result = done_and_exhausted; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_send", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + socket_ops::state_type state_; + ConstBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_send_op : + public reactive_socket_send_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_send_op); + + reactive_socket_send_op(socket_type socket, + socket_ops::state_type state, const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + : reactive_socket_send_op_base(socket, + state, buffers, flags, &reactive_socket_send_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_send_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_sendto_op.hpp b/subprojects/asio/include/asio/detail/reactive_socket_sendto_op.hpp new file mode 100644 index 000000000..c97554d7b --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_sendto_op.hpp @@ -0,0 +1,130 @@ +// +// detail/reactive_socket_sendto_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_sendto_op_base : public reactor_op +{ +public: + reactive_socket_sendto_op_base(socket_type socket, + const ConstBufferSequence& buffers, const Endpoint& endpoint, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_sendto_op_base::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + destination_(endpoint), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_sendto_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + status result = socket_ops::non_blocking_sendto(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->destination_.data(), o->destination_.size(), + o->ec_, o->bytes_transferred_) ? done : not_done; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_sendto", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + ConstBufferSequence buffers_; + Endpoint destination_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_sendto_op : + public reactive_socket_sendto_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_sendto_op); + + reactive_socket_sendto_op(socket_type socket, + const ConstBufferSequence& buffers, const Endpoint& endpoint, + socket_base::message_flags flags, Handler& handler) + : reactive_socket_sendto_op_base(socket, + buffers, endpoint, flags, &reactive_socket_sendto_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_sendto_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_service.hpp b/subprojects/asio/include/asio/detail/reactive_socket_service.hpp new file mode 100644 index 000000000..15a4fdb58 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_service.hpp @@ -0,0 +1,526 @@ +// +// detail/reactive_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_socket_accept_op.hpp" +#include "asio/detail/reactive_socket_connect_op.hpp" +#include "asio/detail/reactive_socket_recvfrom_op.hpp" +#include "asio/detail/reactive_socket_sendto_op.hpp" +#include "asio/detail/reactive_socket_service_base.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_service : + public service_base >, + public reactive_socket_service_base +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct implementation_type : + reactive_socket_service_base::base_implementation_type + { + // Default constructor. + implementation_type() + : protocol_(endpoint_type().protocol()) + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // Constructor. + reactive_socket_service(asio::io_context& io_context) + : service_base >(io_context), + reactive_socket_service_base(io_context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + reactive_socket_service_base& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type& impl, + reactive_socket_service&, + typename reactive_socket_service< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) + impl.protocol_ = protocol; + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + if (!do_assign(impl, protocol.type(), native_socket, ec)) + impl.protocol_ = protocol; + return ec; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type& impl) + { + return impl.socket_; + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, + endpoint.data(), &addr_len, false, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_sendto_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, buffers, destination, flags, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); + + if (!ec) + sender_endpoint.resize(addr_len); + + return bytes_recvd; + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags, + asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvfrom_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + int protocol = impl.protocol_.type(); + p.p = new (p.v) op(impl.socket_, protocol, + buffers, sender_endpoint, flags, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from(null_buffers)")); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type& impl, + Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) + { + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return ec; + } + +#if defined(ASIO_HAS_MOVE) + // Accept a new connection. + typename Protocol::socket accept(implementation_type& impl, + io_context* peer_io_context, endpoint_type* peer_endpoint, + asio::error_code& ec) + { + typename Protocol::socket peer( + peer_io_context ? *peer_io_context : io_context_); + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return peer; + } +#endif // defined(ASIO_HAS_MOVE) + + // Start an asynchronous accept. The peer and peer_endpoint objects must be + // valid until the accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_accept_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, impl.state_, peer, + impl.protocol_, peer_endpoint, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, peer.is_open()); + p.v = p.p = 0; + } + +#if defined(ASIO_HAS_MOVE) + // Start an asynchronous accept. The peer_endpoint object must be valid until + // the accept's handler is invoked. + template + void async_accept(implementation_type& impl, + asio::io_context* peer_io_context, + endpoint_type* peer_endpoint, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_move_accept_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(peer_io_context ? *peer_io_context : io_context_, + impl.socket_, impl.state_, impl.protocol_, peer_endpoint, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, false); + p.v = p.p = 0; + } +#endif // defined(ASIO_HAS_MOVE) + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + socket_ops::sync_connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_connect_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_connect")); + + start_connect_op(impl, p.p, is_continuation, + peer_endpoint.data(), peer_endpoint.size()); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_socket_service_base.hpp b/subprojects/asio/include/asio/detail/reactive_socket_service_base.hpp new file mode 100644 index 000000000..ccd497e80 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_socket_service_base.hpp @@ -0,0 +1,511 @@ +// +// detail/reactive_socket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) \ + && !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_socket_recv_op.hpp" +#include "asio/detail/reactive_socket_recvmsg_op.hpp" +#include "asio/detail/reactive_socket_send_op.hpp" +#include "asio/detail/reactive_wait_op.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_socket_service_base +{ +public: + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + ASIO_DECL reactive_socket_service_base( + asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type& impl); + + // Move-construct a new socket implementation. + ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl); + + // Move-assign from another socket implementation. + ASIO_DECL void base_move_assign(base_implementation_type& impl, + reactive_socket_service_base& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Release ownership of the socket. + ASIO_DECL socket_type release( + base_implementation_type& impl, asio::error_code& ec); + + // Get the native socket representation. + native_handle_type native_handle(base_implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + ASIO_DECL asio::error_code cancel( + base_implementation_type& impl, asio::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(base_implementation_type& impl, + int backlog, asio::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_user_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Wait for the socket to become ready to read, ready to write, or to have + // pending error conditions. + asio::error_code wait(base_implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + switch (w) + { + case socket_base::wait_read: + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_write: + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_error: + socket_ops::poll_error(impl.socket_, impl.state_, -1, ec); + break; + default: + ec = asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the socket to become ready to read, ready to + // write, or to have pending error conditions. + template + void async_wait(base_implementation_type& impl, + socket_base::wait_type w, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_wait_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_wait")); + + int op_type; + switch (w) + { + case socket_base::wait_read: + op_type = reactor::read_op; + break; + case socket_base::wait_write: + op_type = reactor::write_op; + break; + case socket_base::wait_error: + op_type = reactor::except_op; + break; + default: + p.p->ec_ = asio::error::invalid_argument; + reactor_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + return; + } + + start_op(impl, op_type, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Send the given data to the peer. + template + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_send_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter::all_empty(buffers))); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recv_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (flags & socket_base::message_out_of_band) == 0, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter::all_empty(buffers))); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive(null_buffers)")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template + size_t receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recvmsg(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), in_flags, out_flags, ec); + } + + // Wait until data can be received without blocking. + size_t receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvmsg_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, buffers, in_flags, out_flags, handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags")); + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (in_flags & socket_base::message_out_of_band) == 0, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags(null_buffers)")); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + +protected: + // Open a new socket implementation. + ASIO_DECL asio::error_code do_open( + base_implementation_type& impl, int af, + int type, int protocol, asio::error_code& ec); + + // Assign a native socket to a socket implementation. + ASIO_DECL asio::error_code do_assign( + base_implementation_type& impl, int type, + const native_handle_type& native_socket, asio::error_code& ec); + + // Start the asynchronous read or write operation. + ASIO_DECL void start_op(base_implementation_type& impl, int op_type, + reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop); + + // Start the asynchronous accept operation. + ASIO_DECL void start_accept_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open); + + // Start the asynchronous connect operation. + ASIO_DECL void start_connect_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen); + + // The io_context that owns this socket service. + io_context& io_context_; + + // The selector that performs event demultiplexing for the service. + reactor& reactor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_socket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_HAS_IOCP) + // && !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP diff --git a/subprojects/asio/include/asio/detail/reactive_wait_op.hpp b/subprojects/asio/include/asio/detail/reactive_wait_op.hpp new file mode 100644 index 000000000..616d8b115 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactive_wait_op.hpp @@ -0,0 +1,90 @@ +// +// detail/reactive_wait_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_WAIT_OP_HPP +#define ASIO_DETAIL_REACTIVE_WAIT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_wait_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_wait_op); + + reactive_wait_op(Handler& handler) + : reactor_op(&reactive_wait_op::do_perform, + &reactive_wait_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_wait_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_WAIT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactor.hpp b/subprojects/asio/include/asio/detail/reactor.hpp new file mode 100644 index 000000000..47502764f --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactor.hpp @@ -0,0 +1,32 @@ +// +// detail/reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_HPP +#define ASIO_DETAIL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/reactor_fwd.hpp" + +#if defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor.hpp" +#elif defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_reactor.hpp" +#else +# include "asio/detail/select_reactor.hpp" +#endif + +#endif // ASIO_DETAIL_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/reactor_fwd.hpp b/subprojects/asio/include/asio/detail/reactor_fwd.hpp new file mode 100644 index 000000000..a4555c3db --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactor_fwd.hpp @@ -0,0 +1,40 @@ +// +// detail/reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_FWD_HPP +#define ASIO_DETAIL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) +typedef class null_reactor reactor; +#elif defined(ASIO_HAS_IOCP) +typedef class select_reactor reactor; +#elif defined(ASIO_HAS_EPOLL) +typedef class epoll_reactor reactor; +#elif defined(ASIO_HAS_KQUEUE) +typedef class kqueue_reactor reactor; +#elif defined(ASIO_HAS_DEV_POLL) +typedef class dev_poll_reactor reactor; +#else +typedef class select_reactor reactor; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_REACTOR_FWD_HPP diff --git a/subprojects/asio/include/asio/detail/reactor_op.hpp b/subprojects/asio/include/asio/detail/reactor_op.hpp new file mode 100644 index 000000000..faad1a775 --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactor_op.hpp @@ -0,0 +1,65 @@ +// +// detail/reactor_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_OP_HPP +#define ASIO_DETAIL_REACTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactor_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + + // The number of bytes transferred, to be passed to the completion handler. + std::size_t bytes_transferred_; + + // Status returned by perform function. May be used to decide whether it is + // worth performing more operations on the descriptor immediately. + enum status { not_done, done, done_and_exhausted }; + + // Perform the operation. Returns true if it is finished. + status perform() + { + return perform_func_(this); + } + +protected: + typedef status (*perform_func_type)(reactor_op*); + + reactor_op(perform_func_type perform_func, func_type complete_func) + : operation(complete_func), + bytes_transferred_(0), + perform_func_(perform_func) + { + } + +private: + perform_func_type perform_func_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_HPP diff --git a/subprojects/asio/include/asio/detail/reactor_op_queue.hpp b/subprojects/asio/include/asio/detail/reactor_op_queue.hpp new file mode 100644 index 000000000..898f31d1c --- /dev/null +++ b/subprojects/asio/include/asio/detail/reactor_op_queue.hpp @@ -0,0 +1,168 @@ +// +// detail/reactor_op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_OP_QUEUE_HPP +#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactor_op_queue + : private noncopyable +{ +public: + typedef Descriptor key_type; + + struct mapped_type : op_queue + { + mapped_type() {} + mapped_type(const mapped_type&) {} + void operator=(const mapped_type&) {} + }; + + typedef typename hash_map::value_type value_type; + typedef typename hash_map::iterator iterator; + + // Constructor. + reactor_op_queue() + : operations_() + { + } + + // Obtain iterators to all registered descriptors. + iterator begin() { return operations_.begin(); } + iterator end() { return operations_.end(); } + + // Add a new operation to the queue. Returns true if this is the only + // operation for the given descriptor, in which case the reactor's event + // demultiplexing function call may need to be interrupted and restarted. + bool enqueue_operation(Descriptor descriptor, reactor_op* op) + { + std::pair entry = + operations_.insert(value_type(descriptor, mapped_type())); + entry.first->second.push(op); + return entry.second; + } + + // Cancel all operations associated with the descriptor identified by the + // supplied iterator. Any operations pending for the descriptor will be + // cancelled. Returns true if any operations were cancelled, in which case + // the reactor's event demultiplexing function may need to be interrupted and + // restarted. + bool cancel_operations(iterator i, op_queue& ops, + const asio::error_code& ec = + asio::error::operation_aborted) + { + if (i != operations_.end()) + { + while (reactor_op* op = i->second.front()) + { + op->ec_ = ec; + i->second.pop(); + ops.push(op); + } + operations_.erase(i); + return true; + } + + return false; + } + + // Cancel all operations associated with the descriptor. Any operations + // pending for the descriptor will be cancelled. Returns true if any + // operations were cancelled, in which case the reactor's event + // demultiplexing function may need to be interrupted and restarted. + bool cancel_operations(Descriptor descriptor, op_queue& ops, + const asio::error_code& ec = + asio::error::operation_aborted) + { + return this->cancel_operations(operations_.find(descriptor), ops, ec); + } + + // Whether there are no operations in the queue. + bool empty() const + { + return operations_.empty(); + } + + // Determine whether there are any operations associated with the descriptor. + bool has_operation(Descriptor descriptor) const + { + return operations_.find(descriptor) != operations_.end(); + } + + // Perform the operations corresponding to the descriptor identified by the + // supplied iterator. Returns true if there are still unfinished operations + // queued for the descriptor. + bool perform_operations(iterator i, op_queue& ops) + { + if (i != operations_.end()) + { + while (reactor_op* op = i->second.front()) + { + if (op->perform()) + { + i->second.pop(); + ops.push(op); + } + else + { + return true; + } + } + operations_.erase(i); + } + return false; + } + + // Perform the operations corresponding to the descriptor. Returns true if + // there are still unfinished operations queued for the descriptor. + bool perform_operations(Descriptor descriptor, op_queue& ops) + { + return this->perform_operations(operations_.find(descriptor), ops); + } + + // Get all operations owned by the queue. + void get_all_operations(op_queue& ops) + { + iterator i = operations_.begin(); + while (i != operations_.end()) + { + iterator op_iter = i++; + ops.push(op_iter->second); + operations_.erase(op_iter); + } + } + +private: + // The operations that are currently executing asynchronously. + hash_map operations_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP diff --git a/subprojects/asio/include/asio/detail/recycling_allocator.hpp b/subprojects/asio/include/asio/detail/recycling_allocator.hpp new file mode 100644 index 000000000..964af1da6 --- /dev/null +++ b/subprojects/asio/include/asio/detail/recycling_allocator.hpp @@ -0,0 +1,104 @@ +// +// detail/recycling_allocator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RECYCLING_ALLOCATOR_HPP +#define ASIO_DETAIL_RECYCLING_ALLOCATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/thread_context.hpp" +#include "asio/detail/thread_info_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class recycling_allocator +{ +public: + typedef T value_type; + + template + struct rebind + { + typedef recycling_allocator other; + }; + + recycling_allocator() + { + } + + template + recycling_allocator(const recycling_allocator&) + { + } + + T* allocate(std::size_t n) + { + typedef thread_context::thread_call_stack call_stack; + void* p = thread_info_base::allocate(call_stack::top(), sizeof(T) * n); + return static_cast(p); + } + + void deallocate(T* p, std::size_t n) + { + typedef thread_context::thread_call_stack call_stack; + thread_info_base::deallocate(call_stack::top(), p, sizeof(T) * n); + } +}; + +template <> +class recycling_allocator +{ +public: + typedef void value_type; + + template + struct rebind + { + typedef recycling_allocator other; + }; + + recycling_allocator() + { + } + + template + recycling_allocator(const recycling_allocator&) + { + } +}; + +template +struct get_recycling_allocator +{ + typedef Allocator type; + static type get(const Allocator& a) { return a; } +}; + +template +struct get_recycling_allocator > +{ + typedef recycling_allocator type; + static type get(const std::allocator&) { return type(); } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RECYCLING_ALLOCATOR_HPP diff --git a/subprojects/asio/include/asio/detail/regex_fwd.hpp b/subprojects/asio/include/asio/detail/regex_fwd.hpp new file mode 100644 index 000000000..dcf7d06e3 --- /dev/null +++ b/subprojects/asio/include/asio/detail/regex_fwd.hpp @@ -0,0 +1,35 @@ +// +// detail/regex_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REGEX_FWD_HPP +#define ASIO_DETAIL_REGEX_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if defined(ASIO_HAS_BOOST_REGEX) + +#include +#include + +namespace boost { + +template +struct sub_match; + +template +class match_results; + +} // namespace boost + +#endif // defined(ASIO_HAS_BOOST_REGEX) + +#endif // ASIO_DETAIL_REGEX_FWD_HPP diff --git a/subprojects/asio/include/asio/detail/resolve_endpoint_op.hpp b/subprojects/asio/include/asio/detail/resolve_endpoint_op.hpp new file mode 100644 index 000000000..8eee2f98b --- /dev/null +++ b/subprojects/asio/include/asio/detail/resolve_endpoint_op.hpp @@ -0,0 +1,122 @@ +// +// detail/resolve_endpoint_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP +#define ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/resolve_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolve_endpoint_op : public resolve_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(resolve_endpoint_op); + + typedef typename Protocol::endpoint endpoint_type; + typedef asio::ip::basic_resolver_results results_type; + + resolve_endpoint_op(socket_ops::weak_cancel_token_type cancel_token, + const endpoint_type& endpoint, io_context_impl& ioc, Handler& handler) + : resolve_op(&resolve_endpoint_op::do_complete), + cancel_token_(cancel_token), + endpoint_(endpoint), + io_context_impl_(ioc), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the operation object. + resolve_endpoint_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + if (owner && owner != &o->io_context_impl_) + { + // The operation is being run on the worker io_context. Time to perform + // the resolver operation. + + // Perform the blocking endpoint resolution operation. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + socket_ops::background_getnameinfo(o->cancel_token_, o->endpoint_.data(), + o->endpoint_.size(), host_name, NI_MAXHOST, service_name, NI_MAXSERV, + o->endpoint_.protocol().type(), o->ec_); + o->results_ = results_type::create(o->endpoint_, host_name, service_name); + + // Pass operation back to main io_context for completion. + o->io_context_impl_.post_deferred_completion(o); + p.v = p.p = 0; + } + else + { + // The operation has been returned to the main io_context. The completion + // handler is ready to be delivered. + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an upcall, + // a sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->results_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + endpoint_type endpoint_; + io_context_impl& io_context_impl_; + Handler handler_; + results_type results_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/resolve_op.hpp b/subprojects/asio/include/asio/detail/resolve_op.hpp new file mode 100644 index 000000000..a528aa825 --- /dev/null +++ b/subprojects/asio/include/asio/detail/resolve_op.hpp @@ -0,0 +1,45 @@ +// +// detail/resolve_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVE_OP_HPP +#define ASIO_DETAIL_RESOLVE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolve_op : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + +protected: + resolve_op(func_type complete_func) + : operation(complete_func) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVE_OP_HPP diff --git a/subprojects/asio/include/asio/detail/resolve_query_op.hpp b/subprojects/asio/include/asio/detail/resolve_query_op.hpp new file mode 100644 index 000000000..0ec5a3210 --- /dev/null +++ b/subprojects/asio/include/asio/detail/resolve_query_op.hpp @@ -0,0 +1,134 @@ +// +// detail/resolve_query_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVE_QUERY_OP_HPP +#define ASIO_DETAIL_RESOLVE_QUERY_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/resolve_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolve_query_op : public resolve_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(resolve_query_op); + + typedef asio::ip::basic_resolver_query query_type; + typedef asio::ip::basic_resolver_results results_type; + + resolve_query_op(socket_ops::weak_cancel_token_type cancel_token, + const query_type& query, io_context_impl& ioc, Handler& handler) + : resolve_op(&resolve_query_op::do_complete), + cancel_token_(cancel_token), + query_(query), + io_context_impl_(ioc), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + addrinfo_(0) + { + handler_work::start(handler_); + } + + ~resolve_query_op() + { + if (addrinfo_) + socket_ops::freeaddrinfo(addrinfo_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the operation object. + resolve_query_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + if (owner && owner != &o->io_context_impl_) + { + // The operation is being run on the worker io_context. Time to perform + // the resolver operation. + + // Perform the blocking host resolution operation. + socket_ops::background_getaddrinfo(o->cancel_token_, + o->query_.host_name().c_str(), o->query_.service_name().c_str(), + o->query_.hints(), &o->addrinfo_, o->ec_); + + // Pass operation back to main io_context for completion. + o->io_context_impl_.post_deferred_completion(o); + p.v = p.p = 0; + } + else + { + // The operation has been returned to the main io_context. The completion + // handler is ready to be delivered. + + // Take ownership of the operation's outstanding work. + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an upcall, + // a sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, results_type()); + p.h = asio::detail::addressof(handler.handler_); + if (o->addrinfo_) + { + handler.arg2_ = results_type::create(o->addrinfo_, + o->query_.host_name(), o->query_.service_name()); + } + p.reset(); + + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + query_type query_; + io_context_impl& io_context_impl_; + Handler handler_; + asio::detail::addrinfo_type* addrinfo_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVE_QUERY_OP_HPP diff --git a/subprojects/asio/include/asio/detail/resolver_service.hpp b/subprojects/asio/include/asio/detail/resolver_service.hpp new file mode 100644 index 000000000..11a94d135 --- /dev/null +++ b/subprojects/asio/include/asio/detail/resolver_service.hpp @@ -0,0 +1,145 @@ +// +// detail/resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/detail/concurrency_hint.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/resolve_endpoint_op.hpp" +#include "asio/detail/resolve_query_op.hpp" +#include "asio/detail/resolver_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolver_service : + public service_base >, + public resolver_service_base +{ +public: + // The implementation type of the resolver. A cancellation token is used to + // indicate to the background thread that the operation has been cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The query type. + typedef asio::ip::basic_resolver_query query_type; + + // The results type. + typedef asio::ip::basic_resolver_results results_type; + + // Constructor. + resolver_service(asio::io_context& io_context) + : service_base >(io_context), + resolver_service_base(io_context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Perform any fork-related housekeeping. + void notify_fork(asio::io_context::fork_event fork_ev) + { + this->base_notify_fork(fork_ev); + } + + // Resolve a query to a list of entries. + results_type resolve(implementation_type&, const query_type& query, + asio::error_code& ec) + { + asio::detail::addrinfo_type* address_info = 0; + + socket_ops::getaddrinfo(query.host_name().c_str(), + query.service_name().c_str(), query.hints(), &address_info, ec); + auto_addrinfo auto_address_info(address_info); + + return ec ? results_type() : results_type::create( + address_info, query.host_name(), query.service_name()); + } + + // Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, + const query_type& query, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef resolve_query_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl, query, io_context_impl_, handler); + + ASIO_HANDLER_CREATION((io_context_impl_.context(), + *p.p, "resolver", &impl, 0, "async_resolve")); + + start_resolve_op(p.p); + p.v = p.p = 0; + } + + // Resolve an endpoint to a list of entries. + results_type resolve(implementation_type&, + const endpoint_type& endpoint, asio::error_code& ec) + { + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + socket_ops::sync_getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, + endpoint.protocol().type(), ec); + + return ec ? results_type() : results_type::create( + endpoint, host_name, service_name); + } + + // Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type& impl, + const endpoint_type& endpoint, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef resolve_endpoint_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl, endpoint, io_context_impl_, handler); + + ASIO_HANDLER_CREATION((io_context_impl_.context(), + *p.p, "resolver", &impl, 0, "async_resolve")); + + start_resolve_op(p.p); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/resolver_service_base.hpp b/subprojects/asio/include/asio/detail/resolver_service_base.hpp new file mode 100644 index 000000000..10a792241 --- /dev/null +++ b/subprojects/asio/include/asio/detail/resolver_service_base.hpp @@ -0,0 +1,140 @@ +// +// detail/resolver_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/resolve_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/thread.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolver_service_base +{ +public: + // The implementation type of the resolver. A cancellation token is used to + // indicate to the background thread that the operation has been cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; + + // Constructor. + ASIO_DECL resolver_service_base(asio::io_context& io_context); + + // Destructor. + ASIO_DECL ~resolver_service_base(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Perform any fork-related housekeeping. + ASIO_DECL void base_notify_fork( + asio::io_context::fork_event fork_ev); + + // Construct a new resolver implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Destroy a resolver implementation. + ASIO_DECL void destroy(implementation_type&); + + // Move-construct a new resolver implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl); + + // Move-assign from another resolver implementation. + ASIO_DECL void move_assign(implementation_type& impl, + resolver_service_base& other_service, + implementation_type& other_impl); + + // Cancel pending asynchronous operations. + ASIO_DECL void cancel(implementation_type& impl); + +protected: + // Helper function to start an asynchronous resolve operation. + ASIO_DECL void start_resolve_op(resolve_op* op); + +#if !defined(ASIO_WINDOWS_RUNTIME) + // Helper class to perform exception-safe cleanup of addrinfo objects. + class auto_addrinfo + : private asio::detail::noncopyable + { + public: + explicit auto_addrinfo(asio::detail::addrinfo_type* ai) + : ai_(ai) + { + } + + ~auto_addrinfo() + { + if (ai_) + socket_ops::freeaddrinfo(ai_); + } + + operator asio::detail::addrinfo_type*() + { + return ai_; + } + + private: + asio::detail::addrinfo_type* ai_; + }; +#endif // !defined(ASIO_WINDOWS_RUNTIME) + + // Helper class to run the work io_context in a thread. + class work_io_context_runner; + + // Start the work thread if it's not already running. + ASIO_DECL void start_work_thread(); + + // The io_context implementation used to post completions. + io_context_impl& io_context_impl_; + +private: + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Private io_context used for performing asynchronous host resolution. + asio::detail::scoped_ptr work_io_context_; + + // The work io_context implementation used to post completions. + io_context_impl& work_io_context_impl_; + + // Work for the private io_context to perform. + asio::executor_work_guard< + asio::io_context::executor_type> work_; + + // Thread used for running the work io_context's run loop. + asio::detail::scoped_ptr work_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/resolver_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP diff --git a/subprojects/asio/include/asio/detail/scheduler.hpp b/subprojects/asio/include/asio/detail/scheduler.hpp new file mode 100644 index 000000000..10c29b7ed --- /dev/null +++ b/subprojects/asio/include/asio/detail/scheduler.hpp @@ -0,0 +1,213 @@ +// +// detail/scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCHEDULER_HPP +#define ASIO_DETAIL_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/error_code.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/conditionally_enabled_event.hpp" +#include "asio/detail/conditionally_enabled_mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_fwd.hpp" +#include "asio/detail/scheduler_operation.hpp" +#include "asio/detail/thread_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct scheduler_thread_info; + +class scheduler + : public execution_context_service_base, + public thread_context +{ +public: + typedef scheduler_operation operation; + + // Constructor. Specifies the number of concurrent threads that are likely to + // run the scheduler. If set to 1 certain optimisation are performed. + ASIO_DECL scheduler(asio::execution_context& ctx, + int concurrency_hint = 0); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Initialise the task, if required. + ASIO_DECL void init_task(); + + // Run the event loop until interrupted or no more work. + ASIO_DECL std::size_t run(asio::error_code& ec); + + // Run until interrupted or one operation is performed. + ASIO_DECL std::size_t run_one(asio::error_code& ec); + + // Run until timeout, interrupted, or one operation is performed. + ASIO_DECL std::size_t wait_one( + long usec, asio::error_code& ec); + + // Poll for operations without blocking. + ASIO_DECL std::size_t poll(asio::error_code& ec); + + // Poll for one operation without blocking. + ASIO_DECL std::size_t poll_one(asio::error_code& ec); + + // Interrupt the event processing loop. + ASIO_DECL void stop(); + + // Determine whether the scheduler is stopped. + ASIO_DECL bool stopped() const; + + // Restart in preparation for a subsequent run invocation. + ASIO_DECL void restart(); + + // Notify that some work has started. + void work_started() + { + ++outstanding_work_; + } + + // Used to compensate for a forthcoming work_finished call. Must be called + // from within a scheduler-owned thread. + ASIO_DECL void compensating_work_started(); + + // Notify that some work has finished. + void work_finished() + { + if (--outstanding_work_ == 0) + stop(); + } + + // Return whether a handler can be dispatched immediately. + bool can_dispatch() + { + return thread_call_stack::contains(this) != 0; + } + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() has not yet been called for the operation. + ASIO_DECL void post_immediate_completion( + operation* op, bool is_continuation); + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operation. + ASIO_DECL void post_deferred_completion(operation* op); + + // Request invocation of the given operations and return immediately. Assumes + // that work_started() was previously called for each operation. + ASIO_DECL void post_deferred_completions(op_queue& ops); + + // Enqueue the given operation following a failed attempt to dispatch the + // operation for immediate invocation. + ASIO_DECL void do_dispatch(operation* op); + + // Process unfinished operations as part of a shutdownoperation. Assumes that + // work_started() was previously called for the operations. + ASIO_DECL void abandon_operations(op_queue& ops); + + // Get the concurrency hint that was used to initialise the scheduler. + int concurrency_hint() const + { + return concurrency_hint_; + } + +private: + // The mutex type used by this scheduler. + typedef conditionally_enabled_mutex mutex; + + // The event type used by this scheduler. + typedef conditionally_enabled_event event; + + // Structure containing thread-specific data. + typedef scheduler_thread_info thread_info; + + // Run at most one operation. May block. + ASIO_DECL std::size_t do_run_one(mutex::scoped_lock& lock, + thread_info& this_thread, const asio::error_code& ec); + + // Run at most one operation with a timeout. May block. + ASIO_DECL std::size_t do_wait_one(mutex::scoped_lock& lock, + thread_info& this_thread, long usec, const asio::error_code& ec); + + // Poll for at most one operation. + ASIO_DECL std::size_t do_poll_one(mutex::scoped_lock& lock, + thread_info& this_thread, const asio::error_code& ec); + + // Stop the task and all idle threads. + ASIO_DECL void stop_all_threads(mutex::scoped_lock& lock); + + // Wake a single idle thread, or the task, and always unlock the mutex. + ASIO_DECL void wake_one_thread_and_unlock( + mutex::scoped_lock& lock); + + // Helper class to perform task-related operations on block exit. + struct task_cleanup; + friend struct task_cleanup; + + // Helper class to call work-related operations on block exit. + struct work_cleanup; + friend struct work_cleanup; + + // Whether to optimise for single-threaded use cases. + const bool one_thread_; + + // Mutex to protect access to internal data. + mutable mutex mutex_; + + // Event to wake up blocked threads. + event wakeup_event_; + + // The task to be run by this service. + reactor* task_; + + // Operation object to represent the position of the task in the queue. + struct task_operation : operation + { + task_operation() : operation(0) {} + } task_operation_; + + // Whether the task has been interrupted. + bool task_interrupted_; + + // The count of unfinished work. + atomic_count outstanding_work_; + + // The queue of handlers that are ready to be delivered. + op_queue op_queue_; + + // Flag to indicate that the dispatcher has been stopped. + bool stopped_; + + // Flag to indicate that the dispatcher has been shut down. + bool shutdown_; + + // The concurrency hint used to initialise the scheduler. + const int concurrency_hint_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/scheduler.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SCHEDULER_HPP diff --git a/subprojects/asio/include/asio/detail/scheduler_operation.hpp b/subprojects/asio/include/asio/detail/scheduler_operation.hpp new file mode 100644 index 000000000..1c2ce02ec --- /dev/null +++ b/subprojects/asio/include/asio/detail/scheduler_operation.hpp @@ -0,0 +1,78 @@ +// +// detail/scheduler_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCHEDULER_OPERATION_HPP +#define ASIO_DETAIL_SCHEDULER_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/error_code.hpp" +#include "asio/detail/handler_tracking.hpp" +#include "asio/detail/op_queue.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class scheduler; + +// Base class for all operations. A function pointer is used instead of virtual +// functions to avoid the associated overhead. +class scheduler_operation ASIO_INHERIT_TRACKED_HANDLER +{ +public: + typedef scheduler_operation operation_type; + + void complete(void* owner, const asio::error_code& ec, + std::size_t bytes_transferred) + { + func_(owner, this, ec, bytes_transferred); + } + + void destroy() + { + func_(0, this, asio::error_code(), 0); + } + +protected: + typedef void (*func_type)(void*, + scheduler_operation*, + const asio::error_code&, std::size_t); + + scheduler_operation(func_type func) + : next_(0), + func_(func), + task_result_(0) + { + } + + // Prevents deletion through this type. + ~scheduler_operation() + { + } + +private: + friend class op_queue_access; + scheduler_operation* next_; + func_type func_; +protected: + friend class scheduler; + unsigned int task_result_; // Passed into bytes transferred. +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCHEDULER_OPERATION_HPP diff --git a/subprojects/asio/include/asio/detail/scheduler_thread_info.hpp b/subprojects/asio/include/asio/detail/scheduler_thread_info.hpp new file mode 100644 index 000000000..2ffe013bc --- /dev/null +++ b/subprojects/asio/include/asio/detail/scheduler_thread_info.hpp @@ -0,0 +1,40 @@ +// +// detail/scheduler_thread_info.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCHEDULER_THREAD_INFO_HPP +#define ASIO_DETAIL_SCHEDULER_THREAD_INFO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/op_queue.hpp" +#include "asio/detail/thread_info_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class scheduler; +class scheduler_operation; + +struct scheduler_thread_info : public thread_info_base +{ + op_queue private_op_queue; + long private_outstanding_work; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCHEDULER_THREAD_INFO_HPP diff --git a/subprojects/asio/include/asio/detail/scoped_lock.hpp b/subprojects/asio/include/asio/detail/scoped_lock.hpp new file mode 100644 index 000000000..6cbce3814 --- /dev/null +++ b/subprojects/asio/include/asio/detail/scoped_lock.hpp @@ -0,0 +1,101 @@ +// +// detail/scoped_lock.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCOPED_LOCK_HPP +#define ASIO_DETAIL_SCOPED_LOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to lock and unlock a mutex automatically. +template +class scoped_lock + : private noncopyable +{ +public: + // Tag type used to distinguish constructors. + enum adopt_lock_t { adopt_lock }; + + // Constructor adopts a lock that is already held. + scoped_lock(Mutex& m, adopt_lock_t) + : mutex_(m), + locked_(true) + { + } + + // Constructor acquires the lock. + explicit scoped_lock(Mutex& m) + : mutex_(m) + { + mutex_.lock(); + locked_ = true; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (!locked_) + { + mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + + // Test whether the lock is held. + bool locked() const + { + return locked_; + } + + // Get the underlying mutex. + Mutex& mutex() + { + return mutex_; + } + +private: + // The underlying mutex. + Mutex& mutex_; + + // Whether the mutex is currently locked or unlocked. + bool locked_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCOPED_LOCK_HPP diff --git a/subprojects/asio/include/asio/detail/scoped_ptr.hpp b/subprojects/asio/include/asio/detail/scoped_ptr.hpp new file mode 100644 index 000000000..3449c53b7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/scoped_ptr.hpp @@ -0,0 +1,87 @@ +// +// detail/scoped_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCOPED_PTR_HPP +#define ASIO_DETAIL_SCOPED_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class scoped_ptr +{ +public: + // Constructor. + explicit scoped_ptr(T* p = 0) + : p_(p) + { + } + + // Destructor. + ~scoped_ptr() + { + delete p_; + } + + // Access. + T* get() + { + return p_; + } + + // Access. + T* operator->() + { + return p_; + } + + // Dereference. + T& operator*() + { + return *p_; + } + + // Reset pointer. + void reset(T* p = 0) + { + delete p_; + p_ = p; + } + + // Release ownership of the pointer. + T* release() + { + T* tmp = p_; + p_ = 0; + return tmp; + } + +private: + // Disallow copying and assignment. + scoped_ptr(const scoped_ptr&); + scoped_ptr& operator=(const scoped_ptr&); + + T* p_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCOPED_PTR_HPP diff --git a/subprojects/asio/include/asio/detail/select_interrupter.hpp b/subprojects/asio/include/asio/detail/select_interrupter.hpp new file mode 100644 index 000000000..1a0759918 --- /dev/null +++ b/subprojects/asio/include/asio/detail/select_interrupter.hpp @@ -0,0 +1,46 @@ +// +// detail/select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) +# include "asio/detail/socket_select_interrupter.hpp" +#elif defined(ASIO_HAS_EVENTFD) +# include "asio/detail/eventfd_select_interrupter.hpp" +#else +# include "asio/detail/pipe_select_interrupter.hpp" +#endif + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) +typedef socket_select_interrupter select_interrupter; +#elif defined(ASIO_HAS_EVENTFD) +typedef eventfd_select_interrupter select_interrupter; +#else +typedef pipe_select_interrupter select_interrupter; +#endif + +} // namespace detail +} // namespace asio + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP diff --git a/subprojects/asio/include/asio/detail/select_reactor.hpp b/subprojects/asio/include/asio/detail/select_reactor.hpp new file mode 100644 index 000000000..099654927 --- /dev/null +++ b/subprojects/asio/include/asio/detail/select_reactor.hpp @@ -0,0 +1,238 @@ +// +// detail/select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE) \ + && !defined(ASIO_WINDOWS_RUNTIME)) + +#include +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/thread.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class select_reactor + : public execution_context_service_base +{ +public: +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + enum op_types { read_op = 0, write_op = 1, except_op = 2, + max_select_ops = 3, connect_op = 3, max_ops = 4 }; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + enum op_types { read_op = 0, write_op = 1, except_op = 2, + max_select_ops = 3, connect_op = 1, max_ops = 3 }; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + + // Per-descriptor data. + struct per_descriptor_data + { + }; + + // Constructor. + ASIO_DECL select_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~select_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task, but only if the reactor is not in its own thread. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, bool is_continuation, bool); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data&, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data&); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data(per_descriptor_data&); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run select once until interrupted or events are ready to be dispatched. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the select loop. + ASIO_DECL void interrupt(); + +private: +#if defined(ASIO_HAS_IOCP) + // Run the select loop in the thread. + ASIO_DECL void run_thread(); +#endif // defined(ASIO_HAS_IOCP) + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Get the timeout value for the select call. + ASIO_DECL timeval* get_timeout(long usec, timeval& tv); + + // Cancel all operations associated with the given descriptor. This function + // does not acquire the select_reactor's mutex. + ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec); + + // The scheduler implementation used to post completions. +# if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_type; +# else // defined(ASIO_HAS_IOCP) + typedef class scheduler scheduler_type; +# endif // defined(ASIO_HAS_IOCP) + scheduler_type& scheduler_; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The interrupter is used to break a blocking select call. + select_interrupter interrupter_; + + // The queues of read, write and except operations. + reactor_op_queue op_queue_[max_ops]; + + // The file descriptor sets to be passed to the select system call. + fd_set_adapter fd_sets_[max_select_ops]; + + // The timer queues. + timer_queue_set timer_queues_; + +#if defined(ASIO_HAS_IOCP) + // Helper class to run the reactor loop in a thread. + class thread_function; + friend class thread_function; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; +#endif // defined(ASIO_HAS_IOCP) + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/select_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/select_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE) + // && !defined(ASIO_WINDOWS_RUNTIME)) + +#endif // ASIO_DETAIL_SELECT_REACTOR_HPP diff --git a/subprojects/asio/include/asio/detail/service_registry.hpp b/subprojects/asio/include/asio/detail/service_registry.hpp new file mode 100644 index 000000000..cda63072f --- /dev/null +++ b/subprojects/asio/include/asio/detail/service_registry.hpp @@ -0,0 +1,164 @@ +// +// detail/service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class io_context; + +namespace detail { + +template +class typeid_wrapper {}; + +class service_registry + : private noncopyable +{ +public: + // Constructor. + ASIO_DECL service_registry(execution_context& owner); + + // Destructor. + ASIO_DECL ~service_registry(); + + // Shutdown all services. + ASIO_DECL void shutdown_services(); + + // Destroy all services. + ASIO_DECL void destroy_services(); + + // Notify all services of a fork event. + ASIO_DECL void notify_fork(execution_context::fork_event fork_ev); + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + template + Service& use_service(); + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + // This overload is used for backwards compatibility with services that + // inherit from io_context::service. + template + Service& use_service(io_context& owner); + + // Add a service object. Throws on error, in which case ownership of the + // object is retained by the caller. + template + void add_service(Service* new_service); + + // Check whether a service object of the specified type already exists. + template + bool has_service() const; + +private: + // Initalise a service's key when the key_type typedef is not available. + template + static void init_key(execution_context::service::key& key, ...); + +#if !defined(ASIO_NO_TYPEID) + // Initalise a service's key when the key_type typedef is available. + template + static void init_key(execution_context::service::key& key, + typename enable_if< + is_base_of::value>::type*); +#endif // !defined(ASIO_NO_TYPEID) + + // Initialise a service's key based on its id. + ASIO_DECL static void init_key_from_id( + execution_context::service::key& key, + const execution_context::id& id); + +#if !defined(ASIO_NO_TYPEID) + // Initialise a service's key based on its id. + template + static void init_key_from_id(execution_context::service::key& key, + const service_id& /*id*/); +#endif // !defined(ASIO_NO_TYPEID) + + // Check if a service matches the given id. + ASIO_DECL static bool keys_match( + const execution_context::service::key& key1, + const execution_context::service::key& key2); + + // The type of a factory function used for creating a service instance. + typedef execution_context::service*(*factory_type)(void*); + + // Factory function for creating a service instance. + template + static execution_context::service* create(void* owner); + + // Destroy a service instance. + ASIO_DECL static void destroy(execution_context::service* service); + + // Helper class to manage service pointers. + struct auto_service_ptr; + friend struct auto_service_ptr; + struct auto_service_ptr + { + execution_context::service* ptr_; + ~auto_service_ptr() { destroy(ptr_); } + }; + + // Get the service object corresponding to the specified service key. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + ASIO_DECL execution_context::service* do_use_service( + const execution_context::service::key& key, + factory_type factory, void* owner); + + // Add a service object. Throws on error, in which case ownership of the + // object is retained by the caller. + ASIO_DECL void do_add_service( + const execution_context::service::key& key, + execution_context::service* new_service); + + // Check whether a service object with the specified key already exists. + ASIO_DECL bool do_has_service( + const execution_context::service::key& key) const; + + // Mutex to protect access to internal data. + mutable asio::detail::mutex mutex_; + + // The owner of this service registry and the services it contains. + execution_context& owner_; + + // The first service in the list of contained services. + execution_context::service* first_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/service_registry.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/service_registry.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP diff --git a/subprojects/asio/include/asio/detail/signal_blocker.hpp b/subprojects/asio/include/asio/detail/signal_blocker.hpp new file mode 100644 index 000000000..b68429577 --- /dev/null +++ b/subprojects/asio/include/asio/detail/signal_blocker.hpp @@ -0,0 +1,44 @@ +// +// detail/signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) || defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) || defined(__SYMBIAN32__) +# include "asio/detail/null_signal_blocker.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_signal_blocker.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) || defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) || defined(__SYMBIAN32__) +typedef null_signal_blocker signal_blocker; +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_signal_blocker signal_blocker; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP diff --git a/subprojects/asio/include/asio/detail/signal_handler.hpp b/subprojects/asio/include/asio/detail/signal_handler.hpp new file mode 100644 index 000000000..d1c99105a --- /dev/null +++ b/subprojects/asio/include/asio/detail/signal_handler.hpp @@ -0,0 +1,86 @@ +// +// detail/signal_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_HANDLER_HPP +#define ASIO_DETAIL_SIGNAL_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/signal_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class signal_handler : public signal_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(signal_handler); + + signal_handler(Handler& h) + : signal_op(&signal_handler::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(h)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + signal_handler* h(static_cast(base)); + ptr p = { asio::detail::addressof(h->handler_), h, h }; + handler_work w(h->handler_); + + ASIO_HANDLER_COMPLETION((*h)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(h->handler_, h->ec_, h->signal_number_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_HANDLER_HPP diff --git a/subprojects/asio/include/asio/detail/signal_init.hpp b/subprojects/asio/include/asio/detail/signal_init.hpp new file mode 100644 index 000000000..814ff5196 --- /dev/null +++ b/subprojects/asio/include/asio/detail/signal_init.hpp @@ -0,0 +1,47 @@ +// +// detail/signal_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_INIT_HPP +#define ASIO_DETAIL_SIGNAL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class signal_init +{ +public: + // Constructor. + signal_init() + { + std::signal(Signal, SIG_IGN); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_SIGNAL_INIT_HPP diff --git a/subprojects/asio/include/asio/detail/signal_op.hpp b/subprojects/asio/include/asio/detail/signal_op.hpp new file mode 100644 index 000000000..c4e364c14 --- /dev/null +++ b/subprojects/asio/include/asio/detail/signal_op.hpp @@ -0,0 +1,49 @@ +// +// detail/signal_op.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_OP_HPP +#define ASIO_DETAIL_SIGNAL_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class signal_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + + // The signal number to be passed to the completion handler. + int signal_number_; + +protected: + signal_op(func_type func) + : operation(func), + signal_number_(0) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_OP_HPP diff --git a/subprojects/asio/include/asio/detail/signal_set_service.hpp b/subprojects/asio/include/asio/detail/signal_set_service.hpp new file mode 100644 index 000000000..a18ab7093 --- /dev/null +++ b/subprojects/asio/include/asio/detail/signal_set_service.hpp @@ -0,0 +1,217 @@ +// +// detail/signal_set_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_SET_SERVICE_HPP +#define ASIO_DETAIL_SIGNAL_SET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/signal_handler.hpp" +#include "asio/detail/signal_op.hpp" +#include "asio/detail/socket_types.hpp" + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) +# include "asio/detail/reactor.hpp" +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(NSIG) && (NSIG > 0) +enum { max_signal_number = NSIG }; +#else +enum { max_signal_number = 128 }; +#endif + +extern ASIO_DECL struct signal_state* get_signal_state(); + +extern "C" ASIO_DECL void asio_signal_handler(int signal_number); + +class signal_set_service : + public service_base +{ +public: + // Type used for tracking an individual signal registration. + class registration + { + public: + // Default constructor. + registration() + : signal_number_(0), + queue_(0), + undelivered_(0), + next_in_table_(0), + prev_in_table_(0), + next_in_set_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class signal_set_service; + + // The signal number that is registered. + int signal_number_; + + // The waiting signal handlers. + op_queue* queue_; + + // The number of undelivered signals. + std::size_t undelivered_; + + // Pointers to adjacent registrations in the registrations_ table. + registration* next_in_table_; + registration* prev_in_table_; + + // Link to next registration in the signal set. + registration* next_in_set_; + }; + + // The implementation type of the signal_set. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : signals_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class signal_set_service; + + // The pending signal handlers. + op_queue queue_; + + // Linked list of registered signals. + registration* signals_; + }; + + // Constructor. + ASIO_DECL signal_set_service(asio::io_context& io_context); + + // Destructor. + ASIO_DECL ~signal_set_service(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Perform fork-related housekeeping. + ASIO_DECL void notify_fork( + asio::io_context::fork_event fork_ev); + + // Construct a new signal_set implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Destroy a signal_set implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Add a signal to a signal_set. + ASIO_DECL asio::error_code add(implementation_type& impl, + int signal_number, asio::error_code& ec); + + // Remove a signal to a signal_set. + ASIO_DECL asio::error_code remove(implementation_type& impl, + int signal_number, asio::error_code& ec); + + // Remove all signals from a signal_set. + ASIO_DECL asio::error_code clear(implementation_type& impl, + asio::error_code& ec); + + // Cancel all operations associated with the signal set. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Start an asynchronous operation to wait for a signal to be delivered. + template + void async_wait(implementation_type& impl, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef signal_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((io_context_.context(), + *p.p, "signal_set", &impl, 0, "async_wait")); + + start_wait_op(impl, p.p); + p.v = p.p = 0; + } + + // Deliver notification that a particular signal occurred. + ASIO_DECL static void deliver_signal(int signal_number); + +private: + // Helper function to add a service to the global signal state. + ASIO_DECL static void add_service(signal_set_service* service); + + // Helper function to remove a service from the global signal state. + ASIO_DECL static void remove_service(signal_set_service* service); + + // Helper function to create the pipe descriptors. + ASIO_DECL static void open_descriptors(); + + // Helper function to close the pipe descriptors. + ASIO_DECL static void close_descriptors(); + + // Helper function to start a wait operation. + ASIO_DECL void start_wait_op(implementation_type& impl, signal_op* op); + + // The io_context instance used for dispatching handlers. + io_context_impl& io_context_; + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + // The type used for registering for pipe reactor notifications. + class pipe_read_op; + + // The reactor used for waiting for pipe readiness. + reactor& reactor_; + + // The per-descriptor reactor data used for the pipe. + reactor::per_descriptor_data reactor_data_; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + + // A mapping from signal number to the registered signal sets. + registration* registrations_[max_signal_number]; + + // Pointers to adjacent services in linked list. + signal_set_service* next_; + signal_set_service* prev_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/signal_set_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SIGNAL_SET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/socket_holder.hpp b/subprojects/asio/include/asio/detail/socket_holder.hpp new file mode 100644 index 000000000..99b081f71 --- /dev/null +++ b/subprojects/asio/include/asio/detail/socket_holder.hpp @@ -0,0 +1,98 @@ +// +// detail/socket_holder.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_HOLDER_HPP +#define ASIO_DETAIL_SOCKET_HOLDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Implement the resource acquisition is initialisation idiom for sockets. +class socket_holder + : private noncopyable +{ +public: + // Construct as an uninitialised socket. + socket_holder() + : socket_(invalid_socket) + { + } + + // Construct to take ownership of the specified socket. + explicit socket_holder(socket_type s) + : socket_(s) + { + } + + // Destructor. + ~socket_holder() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::state_type state = 0; + socket_ops::close(socket_, state, true, ec); + } + } + + // Get the underlying socket. + socket_type get() const + { + return socket_; + } + + // Reset to an uninitialised socket. + void reset() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::state_type state = 0; + socket_ops::close(socket_, state, true, ec); + socket_ = invalid_socket; + } + } + + // Reset to take ownership of the specified socket. + void reset(socket_type s) + { + reset(); + socket_ = s; + } + + // Release ownership of the socket. + socket_type release() + { + socket_type tmp = socket_; + socket_ = invalid_socket; + return tmp; + } + +private: + // The underlying socket. + socket_type socket_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_HOLDER_HPP diff --git a/subprojects/asio/include/asio/detail/socket_ops.hpp b/subprojects/asio/include/asio/detail/socket_ops.hpp new file mode 100644 index 000000000..815b0d1c8 --- /dev/null +++ b/subprojects/asio/include/asio/detail/socket_ops.hpp @@ -0,0 +1,337 @@ +// +// detail/socket_ops.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPS_HPP +#define ASIO_DETAIL_SOCKET_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/error_code.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +// Socket state bits. +enum +{ + // The user wants a non-blocking socket. + user_set_non_blocking = 1, + + // The socket has been set non-blocking. + internal_non_blocking = 2, + + // Helper "state" used to determine whether the socket is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking, + + // User wants connection_aborted errors, which are disabled by default. + enable_connection_aborted = 4, + + // The user set the linger option. Needs to be checked when closing. + user_set_linger = 8, + + // The socket is stream-oriented. + stream_oriented = 16, + + // The socket is datagram-oriented. + datagram_oriented = 32, + + // The socket may have been dup()-ed. + possible_dup = 64 +}; + +typedef unsigned char state_type; + +struct noop_deleter { void operator()(void*) {} }; +typedef shared_ptr shared_cancel_token_type; +typedef weak_ptr weak_cancel_token_type; + +#if !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL socket_type accept(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL socket_type sync_accept(socket_type s, + state_type state, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_accept(socket_type s, + void* output_buffer, DWORD address_length, + socket_addr_type* addr, std::size_t* addrlen, + socket_type new_socket, asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_accept(socket_type s, + state_type state, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, socket_type& new_socket); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL int bind(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL int close(socket_type s, state_type& state, + bool destruction, asio::error_code& ec); + +ASIO_DECL bool set_user_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec); + +ASIO_DECL bool set_internal_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec); + +ASIO_DECL int shutdown(socket_type s, + int what, asio::error_code& ec); + +ASIO_DECL int connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL void sync_connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_connect(socket_type s, + asio::error_code& ec); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_connect(socket_type s, + asio::error_code& ec); + +ASIO_DECL int socketpair(int af, int type, int protocol, + socket_type sv[2], asio::error_code& ec); + +ASIO_DECL bool sockatmark(socket_type s, asio::error_code& ec); + +ASIO_DECL size_t available(socket_type s, asio::error_code& ec); + +ASIO_DECL int listen(socket_type s, + int backlog, asio::error_code& ec); + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +ASIO_DECL void init_buf(buf& b, void* data, size_t size); + +ASIO_DECL void init_buf(buf& b, const void* data, size_t size); + +ASIO_DECL signed_size_type recv(socket_type s, buf* bufs, + size_t count, int flags, asio::error_code& ec); + +ASIO_DECL size_t sync_recv(socket_type s, state_type state, buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_recv(state_type state, + const weak_cancel_token_type& cancel_token, bool all_empty, + asio::error_code& ec, size_t bytes_transferred); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type recvfrom(socket_type s, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL size_t sync_recvfrom(socket_type s, state_type state, + buf* bufs, size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type recvmsg(socket_type s, buf* bufs, + size_t count, int in_flags, int& out_flags, + asio::error_code& ec); + +ASIO_DECL size_t sync_recvmsg(socket_type s, state_type state, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_recvmsg( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_recvmsg(socket_type s, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type send(socket_type s, const buf* bufs, + size_t count, int flags, asio::error_code& ec); + +ASIO_DECL size_t sync_send(socket_type s, state_type state, + const buf* bufs, size_t count, int flags, + bool all_empty, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_send( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_send(socket_type s, + const buf* bufs, size_t count, int flags, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type sendto(socket_type s, const buf* bufs, + size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL size_t sync_sendto(socket_type s, state_type state, + const buf* bufs, size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +#if !defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_sendto(socket_type s, + const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // !defined(ASIO_HAS_IOCP) + +ASIO_DECL socket_type socket(int af, int type, int protocol, + asio::error_code& ec); + +ASIO_DECL int setsockopt(socket_type s, state_type& state, + int level, int optname, const void* optval, + std::size_t optlen, asio::error_code& ec); + +ASIO_DECL int getsockopt(socket_type s, state_type state, + int level, int optname, void* optval, + size_t* optlen, asio::error_code& ec); + +ASIO_DECL int getpeername(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, bool cached, asio::error_code& ec); + +ASIO_DECL int getsockname(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL int ioctl(socket_type s, state_type& state, + int cmd, ioctl_arg_type* arg, asio::error_code& ec); + +ASIO_DECL int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec); + +ASIO_DECL int poll_read(socket_type s, + state_type state, int msec, asio::error_code& ec); + +ASIO_DECL int poll_write(socket_type s, + state_type state, int msec, asio::error_code& ec); + +ASIO_DECL int poll_error(socket_type s, + state_type state, int msec, asio::error_code& ec); + +ASIO_DECL int poll_connect(socket_type s, + int msec, asio::error_code& ec); + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL const char* inet_ntop(int af, const void* src, char* dest, + size_t length, unsigned long scope_id, asio::error_code& ec); + +ASIO_DECL int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec); + +ASIO_DECL int gethostname(char* name, + int namelen, asio::error_code& ec); + +#if !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec); + +ASIO_DECL asio::error_code background_getaddrinfo( + const weak_cancel_token_type& cancel_token, const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec); + +ASIO_DECL void freeaddrinfo(addrinfo_type* ai); + +ASIO_DECL asio::error_code getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int flags, asio::error_code& ec); + +ASIO_DECL asio::error_code sync_getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec); + +ASIO_DECL asio::error_code background_getnameinfo( + const weak_cancel_token_type& cancel_token, + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec); + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL u_long_type network_to_host_long(u_long_type value); + +ASIO_DECL u_long_type host_to_network_long(u_long_type value); + +ASIO_DECL u_short_type network_to_host_short(u_short_type value); + +ASIO_DECL u_short_type host_to_network_short(u_short_type value); + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/socket_ops.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/subprojects/asio/include/asio/detail/socket_option.hpp b/subprojects/asio/include/asio/detail/socket_option.hpp new file mode 100644 index 000000000..6852d5699 --- /dev/null +++ b/subprojects/asio/include/asio/detail/socket_option.hpp @@ -0,0 +1,316 @@ +// +// detail/socket_option.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPTION_HPP +#define ASIO_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + explicit boolean(bool v) + : value_(v ? 1 : 0) + { + } + + // Set the current value of the boolean. + boolean& operator=(bool v) + { + value_ = v ? 1 : 0; + return *this; + } + + // Get the current value of the boolean. + bool value() const + { + return !!value_; + } + + // Convert to bool. + operator bool() const + { + return !!value_; + } + + // Test for false. + bool operator!() const + { + return !value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the boolean data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the boolean data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the boolean data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the boolean data. + template + void resize(const Protocol&, std::size_t s) + { + // On some platforms (e.g. Windows Vista), the getsockopt function will + // return the size of a boolean socket option as one byte, even though a + // four byte integer was passed in. + switch (s) + { + case sizeof(char): + value_ = *reinterpret_cast(&value_) ? 1 : 0; + break; + case sizeof(value_): + break; + default: + { + std::length_error ex("boolean socket option resize"); + asio::detail::throw_exception(ex); + } + } + } + +private: + int value_; +}; + +// Helper template for implementing integer options. +template +class integer +{ +public: + // Default constructor. + integer() + : value_(0) + { + } + + // Construct with a specific option value. + explicit integer(int v) + : value_(v) + { + } + + // Set the value of the int option. + integer& operator=(int v) + { + value_ = v; + return *this; + } + + // Get the current value of the int option. + int value() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the int data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the int data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the int data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + { + std::length_error ex("integer socket option resize"); + asio::detail::throw_exception(ex); + } + } + +private: + int value_; +}; + +// Helper template for implementing linger options. +template +class linger +{ +public: + // Default constructor. + linger() + { + value_.l_onoff = 0; + value_.l_linger = 0; + } + + // Construct with specific option values. + linger(bool e, int t) + { + enabled(e); + timeout ASIO_PREVENT_MACRO_SUBSTITUTION(t); + } + + // Set the value for whether linger is enabled. + void enabled(bool value) + { + value_.l_onoff = value ? 1 : 0; + } + + // Get the value for whether linger is enabled. + bool enabled() const + { + return value_.l_onoff != 0; + } + + // Set the value for the linger timeout. + void timeout ASIO_PREVENT_MACRO_SUBSTITUTION(int value) + { +#if defined(WIN32) + value_.l_linger = static_cast(value); +#else + value_.l_linger = value; +#endif + } + + // Get the value for the linger timeout. + int timeout ASIO_PREVENT_MACRO_SUBSTITUTION() const + { + return static_cast(value_.l_linger); + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the linger data. + template + detail::linger_type* data(const Protocol&) + { + return &value_; + } + + // Get the address of the linger data. + template + const detail::linger_type* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the linger data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + { + std::length_error ex("linger socket option resize"); + asio::detail::throw_exception(ex); + } + } + +private: + detail::linger_type value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPTION_HPP diff --git a/subprojects/asio/include/asio/detail/socket_select_interrupter.hpp b/subprojects/asio/include/asio/detail/socket_select_interrupter.hpp new file mode 100644 index 000000000..7351070ca --- /dev/null +++ b/subprojects/asio/include/asio/detail/socket_select_interrupter.hpp @@ -0,0 +1,91 @@ +// +// detail/socket_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class socket_select_interrupter +{ +public: + // Constructor. + ASIO_DECL socket_select_interrupter(); + + // Destructor. + ASIO_DECL ~socket_select_interrupter(); + + // Recreate the interrupter's descriptors. Used after a fork. + ASIO_DECL void recreate(); + + // Interrupt the select call. + ASIO_DECL void interrupt(); + + // Reset the select interrupt. Returns true if the call was interrupted. + ASIO_DECL bool reset(); + + // Get the read descriptor to be passed to select. + socket_type read_descriptor() const + { + return read_descriptor_; + } + +private: + // Open the descriptors. Throws on error. + ASIO_DECL void open_descriptors(); + + // Close the descriptors. + ASIO_DECL void close_descriptors(); + + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + socket_type read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + socket_type write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/socket_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP diff --git a/subprojects/asio/include/asio/detail/socket_types.hpp b/subprojects/asio/include/asio/detail/socket_types.hpp new file mode 100644 index 000000000..d354343c0 --- /dev/null +++ b/subprojects/asio/include/asio/detail/socket_types.hpp @@ -0,0 +1,416 @@ +// +// detail/socket_types.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_TYPES_HPP +#define ASIO_DETAIL_SOCKET_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +// Empty. +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# error WinSock.h has already been included +# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# if defined(__BORLANDC__) +# include // Needed for __errno +# if !defined(_WSPIAPI_H_) +# define _WSPIAPI_H_ +# define ASIO_WSPIAPI_H_DEFINED +# endif // !defined(_WSPIAPI_H_) +# endif // defined(__BORLANDC__) +# include +# include +# if defined(WINAPI_FAMILY) +# if ((WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) != 0) +# include +# endif // ((WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) != 0) +# endif // defined(WINAPI_FAMILY) +# if !defined(ASIO_WINDOWS_APP) +# include +# endif // !defined(ASIO_WINDOWS_APP) +# if defined(ASIO_WSPIAPI_H_DEFINED) +# undef _WSPIAPI_H_ +# undef ASIO_WSPIAPI_H_DEFINED +# endif // defined(ASIO_WSPIAPI_H_DEFINED) +# if !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# if defined(UNDER_CE) +# pragma comment(lib, "ws2.lib") +# elif defined(_MSC_VER) || defined(__BORLANDC__) +# pragma comment(lib, "ws2_32.lib") +# if !defined(ASIO_WINDOWS_APP) +# pragma comment(lib, "mswsock.lib") +# endif // !defined(ASIO_WINDOWS_APP) +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# endif // !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# include "asio/detail/old_win_sdk_compat.hpp" +#else +# include +# if (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__linux__) \ + || defined(__EMSCRIPTEN__) +# include +# elif !defined(__SYMBIAN32__) +# include +# endif +# include +# include +# include +# if defined(__hpux) +# include +# endif +# if !defined(__hpux) || defined(__SELECT) +# include +# endif +# include +# include +# include +# include +# if !defined(__SYMBIAN32__) +# include +# endif +# include +# include +# include +# include +# if defined(__sun) +# include +# include +# endif +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS_RUNTIME) +const int max_addr_v4_str_len = 256; +const int max_addr_v6_str_len = 256; +typedef unsigned __int32 u_long_type; +typedef unsigned __int16 u_short_type; +struct in4_addr_type { u_long_type s_addr; }; +struct in4_mreq_type { in4_addr_type imr_multiaddr, imr_interface; }; +struct in6_addr_type { unsigned char s6_addr[16]; }; +struct in6_mreq_type { in6_addr_type ipv6mr_multiaddr; + unsigned long ipv6mr_interface; }; +struct socket_addr_type { int sa_family; }; +struct sockaddr_in4_type { int sin_family; + in4_addr_type sin_addr; u_short_type sin_port; }; +struct sockaddr_in6_type { int sin6_family; + in6_addr_type sin6_addr; u_short_type sin6_port; + u_long_type sin6_flowinfo; u_long_type sin6_scope_id; }; +struct sockaddr_storage_type { int ss_family; + unsigned char ss_bytes[128 - sizeof(int)]; }; +struct addrinfo_type { int ai_flags; + int ai_family, ai_socktype, ai_protocol; + int ai_addrlen; const void* ai_addr; + const char* ai_canonname; addrinfo_type* ai_next; }; +struct linger_type { u_short_type l_onoff, l_linger; }; +typedef u_long_type ioctl_arg_type; +typedef int signed_size_type; +# define ASIO_OS_DEF(c) ASIO_OS_DEF_##c +# define ASIO_OS_DEF_AF_UNSPEC 0 +# define ASIO_OS_DEF_AF_INET 2 +# define ASIO_OS_DEF_AF_INET6 23 +# define ASIO_OS_DEF_SOCK_STREAM 1 +# define ASIO_OS_DEF_SOCK_DGRAM 2 +# define ASIO_OS_DEF_SOCK_RAW 3 +# define ASIO_OS_DEF_SOCK_SEQPACKET 5 +# define ASIO_OS_DEF_IPPROTO_IP 0 +# define ASIO_OS_DEF_IPPROTO_IPV6 41 +# define ASIO_OS_DEF_IPPROTO_TCP 6 +# define ASIO_OS_DEF_IPPROTO_UDP 17 +# define ASIO_OS_DEF_IPPROTO_ICMP 1 +# define ASIO_OS_DEF_IPPROTO_ICMPV6 58 +# define ASIO_OS_DEF_FIONBIO 1 +# define ASIO_OS_DEF_FIONREAD 2 +# define ASIO_OS_DEF_INADDR_ANY 0 +# define ASIO_OS_DEF_MSG_OOB 0x1 +# define ASIO_OS_DEF_MSG_PEEK 0x2 +# define ASIO_OS_DEF_MSG_DONTROUTE 0x4 +# define ASIO_OS_DEF_MSG_EOR 0 // Not supported. +# define ASIO_OS_DEF_SHUT_RD 0x0 +# define ASIO_OS_DEF_SHUT_WR 0x1 +# define ASIO_OS_DEF_SHUT_RDWR 0x2 +# define ASIO_OS_DEF_SOMAXCONN 0x7fffffff +# define ASIO_OS_DEF_SOL_SOCKET 0xffff +# define ASIO_OS_DEF_SO_BROADCAST 0x20 +# define ASIO_OS_DEF_SO_DEBUG 0x1 +# define ASIO_OS_DEF_SO_DONTROUTE 0x10 +# define ASIO_OS_DEF_SO_KEEPALIVE 0x8 +# define ASIO_OS_DEF_SO_LINGER 0x80 +# define ASIO_OS_DEF_SO_OOBINLINE 0x100 +# define ASIO_OS_DEF_SO_SNDBUF 0x1001 +# define ASIO_OS_DEF_SO_RCVBUF 0x1002 +# define ASIO_OS_DEF_SO_SNDLOWAT 0x1003 +# define ASIO_OS_DEF_SO_RCVLOWAT 0x1004 +# define ASIO_OS_DEF_SO_REUSEADDR 0x4 +# define ASIO_OS_DEF_TCP_NODELAY 0x1 +# define ASIO_OS_DEF_IP_MULTICAST_IF 2 +# define ASIO_OS_DEF_IP_MULTICAST_TTL 3 +# define ASIO_OS_DEF_IP_MULTICAST_LOOP 4 +# define ASIO_OS_DEF_IP_ADD_MEMBERSHIP 5 +# define ASIO_OS_DEF_IP_DROP_MEMBERSHIP 6 +# define ASIO_OS_DEF_IP_TTL 7 +# define ASIO_OS_DEF_IPV6_UNICAST_HOPS 4 +# define ASIO_OS_DEF_IPV6_MULTICAST_IF 9 +# define ASIO_OS_DEF_IPV6_MULTICAST_HOPS 10 +# define ASIO_OS_DEF_IPV6_MULTICAST_LOOP 11 +# define ASIO_OS_DEF_IPV6_JOIN_GROUP 12 +# define ASIO_OS_DEF_IPV6_LEAVE_GROUP 13 +# define ASIO_OS_DEF_AI_CANONNAME 0x2 +# define ASIO_OS_DEF_AI_PASSIVE 0x1 +# define ASIO_OS_DEF_AI_NUMERICHOST 0x4 +# define ASIO_OS_DEF_AI_NUMERICSERV 0x8 +# define ASIO_OS_DEF_AI_V4MAPPED 0x800 +# define ASIO_OS_DEF_AI_ALL 0x100 +# define ASIO_OS_DEF_AI_ADDRCONFIG 0x400 +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef SOCKET socket_type; +const SOCKET invalid_socket = INVALID_SOCKET; +const int socket_error_retval = SOCKET_ERROR; +const int max_addr_v4_str_len = 256; +const int max_addr_v6_str_len = 256; +typedef sockaddr socket_addr_type; +typedef in_addr in4_addr_type; +typedef ip_mreq in4_mreq_type; +typedef sockaddr_in sockaddr_in4_type; +# if defined(ASIO_HAS_OLD_WIN_SDK) +typedef in6_addr_emulation in6_addr_type; +typedef ipv6_mreq_emulation in6_mreq_type; +typedef sockaddr_in6_emulation sockaddr_in6_type; +typedef sockaddr_storage_emulation sockaddr_storage_type; +typedef addrinfo_emulation addrinfo_type; +# else +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef addrinfo addrinfo_type; +# endif +typedef ::linger linger_type; +typedef unsigned long ioctl_arg_type; +typedef u_long u_long_type; +typedef u_short u_short_type; +typedef int signed_size_type; +# define ASIO_OS_DEF(c) ASIO_OS_DEF_##c +# define ASIO_OS_DEF_AF_UNSPEC AF_UNSPEC +# define ASIO_OS_DEF_AF_INET AF_INET +# define ASIO_OS_DEF_AF_INET6 AF_INET6 +# define ASIO_OS_DEF_SOCK_STREAM SOCK_STREAM +# define ASIO_OS_DEF_SOCK_DGRAM SOCK_DGRAM +# define ASIO_OS_DEF_SOCK_RAW SOCK_RAW +# define ASIO_OS_DEF_SOCK_SEQPACKET SOCK_SEQPACKET +# define ASIO_OS_DEF_IPPROTO_IP IPPROTO_IP +# define ASIO_OS_DEF_IPPROTO_IPV6 IPPROTO_IPV6 +# define ASIO_OS_DEF_IPPROTO_TCP IPPROTO_TCP +# define ASIO_OS_DEF_IPPROTO_UDP IPPROTO_UDP +# define ASIO_OS_DEF_IPPROTO_ICMP IPPROTO_ICMP +# define ASIO_OS_DEF_IPPROTO_ICMPV6 IPPROTO_ICMPV6 +# define ASIO_OS_DEF_FIONBIO FIONBIO +# define ASIO_OS_DEF_FIONREAD FIONREAD +# define ASIO_OS_DEF_INADDR_ANY INADDR_ANY +# define ASIO_OS_DEF_MSG_OOB MSG_OOB +# define ASIO_OS_DEF_MSG_PEEK MSG_PEEK +# define ASIO_OS_DEF_MSG_DONTROUTE MSG_DONTROUTE +# define ASIO_OS_DEF_MSG_EOR 0 // Not supported on Windows. +# define ASIO_OS_DEF_SHUT_RD SD_RECEIVE +# define ASIO_OS_DEF_SHUT_WR SD_SEND +# define ASIO_OS_DEF_SHUT_RDWR SD_BOTH +# define ASIO_OS_DEF_SOMAXCONN SOMAXCONN +# define ASIO_OS_DEF_SOL_SOCKET SOL_SOCKET +# define ASIO_OS_DEF_SO_BROADCAST SO_BROADCAST +# define ASIO_OS_DEF_SO_DEBUG SO_DEBUG +# define ASIO_OS_DEF_SO_DONTROUTE SO_DONTROUTE +# define ASIO_OS_DEF_SO_KEEPALIVE SO_KEEPALIVE +# define ASIO_OS_DEF_SO_LINGER SO_LINGER +# define ASIO_OS_DEF_SO_OOBINLINE SO_OOBINLINE +# define ASIO_OS_DEF_SO_SNDBUF SO_SNDBUF +# define ASIO_OS_DEF_SO_RCVBUF SO_RCVBUF +# define ASIO_OS_DEF_SO_SNDLOWAT SO_SNDLOWAT +# define ASIO_OS_DEF_SO_RCVLOWAT SO_RCVLOWAT +# define ASIO_OS_DEF_SO_REUSEADDR SO_REUSEADDR +# define ASIO_OS_DEF_TCP_NODELAY TCP_NODELAY +# define ASIO_OS_DEF_IP_MULTICAST_IF IP_MULTICAST_IF +# define ASIO_OS_DEF_IP_MULTICAST_TTL IP_MULTICAST_TTL +# define ASIO_OS_DEF_IP_MULTICAST_LOOP IP_MULTICAST_LOOP +# define ASIO_OS_DEF_IP_ADD_MEMBERSHIP IP_ADD_MEMBERSHIP +# define ASIO_OS_DEF_IP_DROP_MEMBERSHIP IP_DROP_MEMBERSHIP +# define ASIO_OS_DEF_IP_TTL IP_TTL +# define ASIO_OS_DEF_IPV6_UNICAST_HOPS IPV6_UNICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_IF IPV6_MULTICAST_IF +# define ASIO_OS_DEF_IPV6_MULTICAST_HOPS IPV6_MULTICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_LOOP IPV6_MULTICAST_LOOP +# define ASIO_OS_DEF_IPV6_JOIN_GROUP IPV6_JOIN_GROUP +# define ASIO_OS_DEF_IPV6_LEAVE_GROUP IPV6_LEAVE_GROUP +# define ASIO_OS_DEF_AI_CANONNAME AI_CANONNAME +# define ASIO_OS_DEF_AI_PASSIVE AI_PASSIVE +# define ASIO_OS_DEF_AI_NUMERICHOST AI_NUMERICHOST +# if defined(AI_NUMERICSERV) +# define ASIO_OS_DEF_AI_NUMERICSERV AI_NUMERICSERV +# else +# define ASIO_OS_DEF_AI_NUMERICSERV 0 +# endif +# if defined(AI_V4MAPPED) +# define ASIO_OS_DEF_AI_V4MAPPED AI_V4MAPPED +# else +# define ASIO_OS_DEF_AI_V4MAPPED 0 +# endif +# if defined(AI_ALL) +# define ASIO_OS_DEF_AI_ALL AI_ALL +# else +# define ASIO_OS_DEF_AI_ALL 0 +# endif +# if defined(AI_ADDRCONFIG) +# define ASIO_OS_DEF_AI_ADDRCONFIG AI_ADDRCONFIG +# else +# define ASIO_OS_DEF_AI_ADDRCONFIG 0 +# endif +# if defined (_WIN32_WINNT) +const int max_iov_len = 64; +# else +const int max_iov_len = 16; +# endif +#else +typedef int socket_type; +const int invalid_socket = -1; +const int socket_error_retval = -1; +const int max_addr_v4_str_len = INET_ADDRSTRLEN; +#if defined(INET6_ADDRSTRLEN) +const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; +#else // defined(INET6_ADDRSTRLEN) +const int max_addr_v6_str_len = 256; +#endif // defined(INET6_ADDRSTRLEN) +typedef sockaddr socket_addr_type; +typedef in_addr in4_addr_type; +# if defined(__hpux) +// HP-UX doesn't provide ip_mreq when _XOPEN_SOURCE_EXTENDED is defined. +struct in4_mreq_type +{ + struct in_addr imr_multiaddr; + struct in_addr imr_interface; +}; +# else +typedef ip_mreq in4_mreq_type; +# endif +typedef sockaddr_in sockaddr_in4_type; +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef sockaddr_un sockaddr_un_type; +typedef addrinfo addrinfo_type; +typedef ::linger linger_type; +typedef int ioctl_arg_type; +typedef uint32_t u_long_type; +typedef uint16_t u_short_type; +#if defined(ASIO_HAS_SSIZE_T) +typedef ssize_t signed_size_type; +#else // defined(ASIO_HAS_SSIZE_T) +typedef int signed_size_type; +#endif // defined(ASIO_HAS_SSIZE_T) +# define ASIO_OS_DEF(c) ASIO_OS_DEF_##c +# define ASIO_OS_DEF_AF_UNSPEC AF_UNSPEC +# define ASIO_OS_DEF_AF_INET AF_INET +# define ASIO_OS_DEF_AF_INET6 AF_INET6 +# define ASIO_OS_DEF_SOCK_STREAM SOCK_STREAM +# define ASIO_OS_DEF_SOCK_DGRAM SOCK_DGRAM +# define ASIO_OS_DEF_SOCK_RAW SOCK_RAW +# define ASIO_OS_DEF_SOCK_SEQPACKET SOCK_SEQPACKET +# define ASIO_OS_DEF_IPPROTO_IP IPPROTO_IP +# define ASIO_OS_DEF_IPPROTO_IPV6 IPPROTO_IPV6 +# define ASIO_OS_DEF_IPPROTO_TCP IPPROTO_TCP +# define ASIO_OS_DEF_IPPROTO_UDP IPPROTO_UDP +# define ASIO_OS_DEF_IPPROTO_ICMP IPPROTO_ICMP +# define ASIO_OS_DEF_IPPROTO_ICMPV6 IPPROTO_ICMPV6 +# define ASIO_OS_DEF_FIONBIO FIONBIO +# define ASIO_OS_DEF_FIONREAD FIONREAD +# define ASIO_OS_DEF_INADDR_ANY INADDR_ANY +# define ASIO_OS_DEF_MSG_OOB MSG_OOB +# define ASIO_OS_DEF_MSG_PEEK MSG_PEEK +# define ASIO_OS_DEF_MSG_DONTROUTE MSG_DONTROUTE +# define ASIO_OS_DEF_MSG_EOR MSG_EOR +# define ASIO_OS_DEF_SHUT_RD SHUT_RD +# define ASIO_OS_DEF_SHUT_WR SHUT_WR +# define ASIO_OS_DEF_SHUT_RDWR SHUT_RDWR +# define ASIO_OS_DEF_SOMAXCONN SOMAXCONN +# define ASIO_OS_DEF_SOL_SOCKET SOL_SOCKET +# define ASIO_OS_DEF_SO_BROADCAST SO_BROADCAST +# define ASIO_OS_DEF_SO_DEBUG SO_DEBUG +# define ASIO_OS_DEF_SO_DONTROUTE SO_DONTROUTE +# define ASIO_OS_DEF_SO_KEEPALIVE SO_KEEPALIVE +# define ASIO_OS_DEF_SO_LINGER SO_LINGER +# define ASIO_OS_DEF_SO_OOBINLINE SO_OOBINLINE +# define ASIO_OS_DEF_SO_SNDBUF SO_SNDBUF +# define ASIO_OS_DEF_SO_RCVBUF SO_RCVBUF +# define ASIO_OS_DEF_SO_SNDLOWAT SO_SNDLOWAT +# define ASIO_OS_DEF_SO_RCVLOWAT SO_RCVLOWAT +# define ASIO_OS_DEF_SO_REUSEADDR SO_REUSEADDR +# define ASIO_OS_DEF_TCP_NODELAY TCP_NODELAY +# define ASIO_OS_DEF_IP_MULTICAST_IF IP_MULTICAST_IF +# define ASIO_OS_DEF_IP_MULTICAST_TTL IP_MULTICAST_TTL +# define ASIO_OS_DEF_IP_MULTICAST_LOOP IP_MULTICAST_LOOP +# define ASIO_OS_DEF_IP_ADD_MEMBERSHIP IP_ADD_MEMBERSHIP +# define ASIO_OS_DEF_IP_DROP_MEMBERSHIP IP_DROP_MEMBERSHIP +# define ASIO_OS_DEF_IP_TTL IP_TTL +# define ASIO_OS_DEF_IPV6_UNICAST_HOPS IPV6_UNICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_IF IPV6_MULTICAST_IF +# define ASIO_OS_DEF_IPV6_MULTICAST_HOPS IPV6_MULTICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_LOOP IPV6_MULTICAST_LOOP +# define ASIO_OS_DEF_IPV6_JOIN_GROUP IPV6_JOIN_GROUP +# define ASIO_OS_DEF_IPV6_LEAVE_GROUP IPV6_LEAVE_GROUP +# define ASIO_OS_DEF_AI_CANONNAME AI_CANONNAME +# define ASIO_OS_DEF_AI_PASSIVE AI_PASSIVE +# define ASIO_OS_DEF_AI_NUMERICHOST AI_NUMERICHOST +# if defined(AI_NUMERICSERV) +# define ASIO_OS_DEF_AI_NUMERICSERV AI_NUMERICSERV +# else +# define ASIO_OS_DEF_AI_NUMERICSERV 0 +# endif +// Note: QNX Neutrino 6.3 defines AI_V4MAPPED, AI_ALL and AI_ADDRCONFIG but +// does not implement them. Therefore they are specifically excluded here. +# if defined(AI_V4MAPPED) && !defined(__QNXNTO__) +# define ASIO_OS_DEF_AI_V4MAPPED AI_V4MAPPED +# else +# define ASIO_OS_DEF_AI_V4MAPPED 0 +# endif +# if defined(AI_ALL) && !defined(__QNXNTO__) +# define ASIO_OS_DEF_AI_ALL AI_ALL +# else +# define ASIO_OS_DEF_AI_ALL 0 +# endif +# if defined(AI_ADDRCONFIG) && !defined(__QNXNTO__) +# define ASIO_OS_DEF_AI_ADDRCONFIG AI_ADDRCONFIG +# else +# define ASIO_OS_DEF_AI_ADDRCONFIG 0 +# endif +# if defined(IOV_MAX) +const int max_iov_len = IOV_MAX; +# else +// POSIX platforms are not required to define IOV_MAX. +const int max_iov_len = 16; +# endif +#endif +const int custom_socket_option_level = 0xA5100000; +const int enable_connection_aborted_option = 1; +const int always_fail_option = 2; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_TYPES_HPP diff --git a/subprojects/asio/include/asio/detail/solaris_fenced_block.hpp b/subprojects/asio/include/asio/detail/solaris_fenced_block.hpp new file mode 100644 index 000000000..d48f6a374 --- /dev/null +++ b/subprojects/asio/include/asio/detail/solaris_fenced_block.hpp @@ -0,0 +1,62 @@ +// +// detail/solaris_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP +#define ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__sun) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class solaris_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit solaris_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit solaris_fenced_block(full_t) + { + membar_consumer(); + } + + // Destructor. + ~solaris_fenced_block() + { + membar_producer(); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__sun) + +#endif // ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/static_mutex.hpp b/subprojects/asio/include/asio/detail/static_mutex.hpp new file mode 100644 index 000000000..8f2bc02a0 --- /dev/null +++ b/subprojects/asio/include/asio/detail/static_mutex.hpp @@ -0,0 +1,52 @@ +// +// detail/static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STATIC_MUTEX_HPP +#define ASIO_DETAIL_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_static_mutex.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_static_mutex.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_static_mutex.hpp" +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# include "asio/detail/std_static_mutex.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_NULL_STATIC_MUTEX_INIT +#elif defined(ASIO_WINDOWS) +typedef win_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_WIN_STATIC_MUTEX_INIT +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_POSIX_STATIC_MUTEX_INIT +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +typedef std_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_STD_STATIC_MUTEX_INIT +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_STATIC_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/std_event.hpp b/subprojects/asio/include/asio/detail/std_event.hpp new file mode 100644 index 000000000..5639ecdd4 --- /dev/null +++ b/subprojects/asio/include/asio/detail/std_event.hpp @@ -0,0 +1,176 @@ +// +// detail/std_event.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_EVENT_HPP +#define ASIO_DETAIL_STD_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_event + : private noncopyable +{ +public: + // Constructor. + std_event() + : state_(0) + { + } + + // Destructor. + ~std_event() + { + } + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock& lock) + { + this->signal_all(lock); + } + + // Signal all waiters. + template + void signal_all(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ |= 1; + cond_.notify_all(); + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + lock.unlock(); + if (have_waiters) + cond_.notify_one(); + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + if (state_ > 1) + { + lock.unlock(); + cond_.notify_one(); + return true; + } + return false; + } + + // Reset the event. + template + void clear(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ &= ~std::size_t(1); + } + + // Wait for the event to become signalled. + template + void wait(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + unique_lock_adapter u_lock(lock); + while ((state_ & 1) == 0) + { + waiter w(state_); + cond_.wait(u_lock.unique_lock_); + } + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock& lock, long usec) + { + ASIO_ASSERT(lock.locked()); + unique_lock_adapter u_lock(lock); + if ((state_ & 1) == 0) + { + waiter w(state_); + cond_.wait_for(u_lock.unique_lock_, std::chrono::microseconds(usec)); + } + return (state_ & 1) != 0; + } + +private: + // Helper class to temporarily adapt a scoped_lock into a unique_lock so that + // it can be passed to std::condition_variable::wait(). + struct unique_lock_adapter + { + template + explicit unique_lock_adapter(Lock& lock) + : unique_lock_(lock.mutex().mutex_, std::adopt_lock) + { + } + + ~unique_lock_adapter() + { + unique_lock_.release(); + } + + std::unique_lock unique_lock_; + }; + + // Helper to increment and decrement the state to track outstanding waiters. + class waiter + { + public: + explicit waiter(std::size_t& state) + : state_(state) + { + state_ += 2; + } + + ~waiter() + { + state_ -= 2; + } + + private: + std::size_t& state_; + }; + + std::condition_variable cond_; + std::size_t state_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#endif // ASIO_DETAIL_STD_EVENT_HPP diff --git a/subprojects/asio/include/asio/detail/std_fenced_block.hpp b/subprojects/asio/include/asio/detail/std_fenced_block.hpp new file mode 100644 index 000000000..0d8ade028 --- /dev/null +++ b/subprojects/asio/include/asio/detail/std_fenced_block.hpp @@ -0,0 +1,62 @@ +// +// detail/std_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_FENCED_BLOCK_HPP +#define ASIO_DETAIL_STD_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_ATOMIC) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit std_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit std_fenced_block(full_t) + { + std::atomic_thread_fence(std::memory_order_acquire); + } + + // Destructor. + ~std_fenced_block() + { + std::atomic_thread_fence(std::memory_order_release); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_ATOMIC) + +#endif // ASIO_DETAIL_STD_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/std_global.hpp b/subprojects/asio/include/asio/detail/std_global.hpp new file mode 100644 index 000000000..0c5173e6b --- /dev/null +++ b/subprojects/asio/include/asio/detail/std_global.hpp @@ -0,0 +1,70 @@ +// +// detail/std_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_GLOBAL_HPP +#define ASIO_DETAIL_STD_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_CALL_ONCE) + +#include +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct std_global_impl +{ + // Helper function to perform initialisation. + static void do_init() + { + instance_.ptr_ = new T; + } + + // Destructor automatically cleans up the global. + ~std_global_impl() + { + delete ptr_; + } + + static std::once_flag init_once_; + static std_global_impl instance_; + T* ptr_; +}; + +template +std::once_flag std_global_impl::init_once_; + +template +std_global_impl std_global_impl::instance_; + +template +T& std_global() +{ + std::call_once(std_global_impl::init_once_, &std_global_impl::do_init); + return *std_global_impl::instance_.ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_CALL_ONCE) + +#endif // ASIO_DETAIL_STD_GLOBAL_HPP diff --git a/subprojects/asio/include/asio/detail/std_mutex.hpp b/subprojects/asio/include/asio/detail/std_mutex.hpp new file mode 100644 index 000000000..159049e8c --- /dev/null +++ b/subprojects/asio/include/asio/detail/std_mutex.hpp @@ -0,0 +1,73 @@ +// +// detail/std_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_MUTEX_HPP +#define ASIO_DETAIL_STD_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_event; + +class std_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + std_mutex() + { + } + + // Destructor. + ~std_mutex() + { + } + + // Lock the mutex. + void lock() + { + mutex_.lock(); + } + + // Unlock the mutex. + void unlock() + { + mutex_.unlock(); + } + +private: + friend class std_event; + std::mutex mutex_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#endif // ASIO_DETAIL_STD_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/std_static_mutex.hpp b/subprojects/asio/include/asio/detail/std_static_mutex.hpp new file mode 100644 index 000000000..e9c9dc5ee --- /dev/null +++ b/subprojects/asio/include/asio/detail/std_static_mutex.hpp @@ -0,0 +1,81 @@ +// +// detail/std_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_STATIC_MUTEX_HPP +#define ASIO_DETAIL_STD_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_event; + +class std_static_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + std_static_mutex(int) + { + } + + // Destructor. + ~std_static_mutex() + { + } + + // Initialise the mutex. + void init() + { + // Nothing to do. + } + + // Lock the mutex. + void lock() + { + mutex_.lock(); + } + + // Unlock the mutex. + void unlock() + { + mutex_.unlock(); + } + +private: + friend class std_event; + std::mutex mutex_; +}; + +#define ASIO_STD_STATIC_MUTEX_INIT 0 + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#endif // ASIO_DETAIL_STD_STATIC_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/std_thread.hpp b/subprojects/asio/include/asio/detail/std_thread.hpp new file mode 100644 index 000000000..a240308d8 --- /dev/null +++ b/subprojects/asio/include/asio/detail/std_thread.hpp @@ -0,0 +1,71 @@ +// +// detail/std_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_THREAD_HPP +#define ASIO_DETAIL_STD_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_THREAD) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_thread + : private noncopyable +{ +public: + // Constructor. + template + std_thread(Function f, unsigned int = 0) + : thread_(f) + { + } + + // Destructor. + ~std_thread() + { + join(); + } + + // Wait for the thread to exit. + void join() + { + if (thread_.joinable()) + thread_.join(); + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + return std::thread::hardware_concurrency(); + } + +private: + std::thread thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_THREAD) + +#endif // ASIO_DETAIL_STD_THREAD_HPP diff --git a/subprojects/asio/include/asio/detail/strand_executor_service.hpp b/subprojects/asio/include/asio/detail/strand_executor_service.hpp new file mode 100644 index 000000000..67e84274b --- /dev/null +++ b/subprojects/asio/include/asio/detail/strand_executor_service.hpp @@ -0,0 +1,142 @@ +// +// detail/strand_executor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STRAND_EXECUTOR_SERVICE_HPP +#define ASIO_DETAIL_STRAND_EXECUTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/executor_op.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/scheduler_operation.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Default service implementation for a strand. +class strand_executor_service + : public execution_context_service_base +{ +public: + // The underlying implementation of a strand. + class strand_impl + { + public: + ASIO_DECL ~strand_impl(); + + private: + friend class strand_executor_service; + + // Mutex to protect access to internal data. + mutex* mutex_; + + // Indicates whether the strand is currently "locked" by a handler. This + // means that there is a handler upcall in progress, or that the strand + // itself has been scheduled in order to invoke some pending handlers. + bool locked_; + + // Indicates that the strand has been shut down and will accept no further + // handlers. + bool shutdown_; + + // The handlers that are waiting on the strand but should not be run until + // after the next time the strand is scheduled. This queue must only be + // modified while the mutex is locked. + op_queue waiting_queue_; + + // The handlers that are ready to be run. Logically speaking, these are the + // handlers that hold the strand's lock. The ready queue is only modified + // from within the strand and so may be accessed without locking the mutex. + op_queue ready_queue_; + + // Pointers to adjacent handle implementations in linked list. + strand_impl* next_; + strand_impl* prev_; + + // The strand service in where the implementation is held. + strand_executor_service* service_; + }; + + typedef shared_ptr implementation_type; + + // Construct a new strand service for the specified context. + ASIO_DECL explicit strand_executor_service(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Create a new strand_executor implementation. + ASIO_DECL implementation_type create_implementation(); + + // Request invocation of the given function. + template + static void dispatch(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, const Allocator& a); + + // Request invocation of the given function and return immediately. + template + static void post(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, const Allocator& a); + + // Request invocation of the given function and return immediately. + template + static void defer(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, const Allocator& a); + + // Determine whether the strand is running in the current thread. + ASIO_DECL static bool running_in_this_thread( + const implementation_type& impl); + +private: + friend class strand_impl; + template class invoker; + + // Adds a function to the strand. Returns true if it acquires the lock. + ASIO_DECL static bool enqueue(const implementation_type& impl, + scheduler_operation* op); + + // Mutex to protect access to the service-wide state. + mutex mutex_; + + // Number of mutexes shared between all strand objects. + enum { num_mutexes = 193 }; + + // Pool of mutexes. + scoped_ptr mutexes_[num_mutexes]; + + // Extra value used when hashing to prevent recycled memory locations from + // getting the same mutex. + std::size_t salt_; + + // The head of a linked list of all implementations. + strand_impl* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/strand_executor_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/strand_executor_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_STRAND_EXECUTOR_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/strand_service.hpp b/subprojects/asio/include/asio/detail/strand_service.hpp new file mode 100644 index 000000000..edc14a0f7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/strand_service.hpp @@ -0,0 +1,142 @@ +// +// detail/strand_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/scoped_ptr.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Default service implementation for a strand. +class strand_service + : public asio::detail::service_base +{ +private: + // Helper class to re-post the strand on exit. + struct on_do_complete_exit; + + // Helper class to re-post the strand on exit. + struct on_dispatch_exit; + +public: + + // The underlying implementation of a strand. + class strand_impl + : public operation + { + public: + strand_impl(); + + private: + // Only this service will have access to the internal values. + friend class strand_service; + friend struct on_do_complete_exit; + friend struct on_dispatch_exit; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Indicates whether the strand is currently "locked" by a handler. This + // means that there is a handler upcall in progress, or that the strand + // itself has been scheduled in order to invoke some pending handlers. + bool locked_; + + // The handlers that are waiting on the strand but should not be run until + // after the next time the strand is scheduled. This queue must only be + // modified while the mutex is locked. + op_queue waiting_queue_; + + // The handlers that are ready to be run. Logically speaking, these are the + // handlers that hold the strand's lock. The ready queue is only modified + // from within the strand and so may be accessed without locking the mutex. + op_queue ready_queue_; + }; + + typedef strand_impl* implementation_type; + + // Construct a new strand service for the specified io_context. + ASIO_DECL explicit strand_service(asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new strand implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Request the io_context to invoke the given handler. + template + void dispatch(implementation_type& impl, Handler& handler); + + // Request the io_context to invoke the given handler and return immediately. + template + void post(implementation_type& impl, Handler& handler); + + // Determine whether the strand is running in the current thread. + ASIO_DECL bool running_in_this_thread( + const implementation_type& impl) const; + +private: + // Helper function to dispatch a handler. Returns true if the handler should + // be dispatched immediately. + ASIO_DECL bool do_dispatch(implementation_type& impl, operation* op); + + // Helper fiunction to post a handler. + ASIO_DECL void do_post(implementation_type& impl, + operation* op, bool is_continuation); + + ASIO_DECL static void do_complete(void* owner, + operation* base, const asio::error_code& ec, + std::size_t bytes_transferred); + + // The io_context implementation used to post completions. + io_context_impl& io_context_; + + // Mutex to protect access to the array of implementations. + asio::detail::mutex mutex_; + + // Number of implementations shared between all strand objects. +#if defined(ASIO_STRAND_IMPLEMENTATIONS) + enum { num_implementations = ASIO_STRAND_IMPLEMENTATIONS }; +#else // defined(ASIO_STRAND_IMPLEMENTATIONS) + enum { num_implementations = 193 }; +#endif // defined(ASIO_STRAND_IMPLEMENTATIONS) + + // Pool of implementations. + scoped_ptr implementations_[num_implementations]; + + // Extra value used when hashing to prevent recycled memory locations from + // getting the same strand implementation. + std::size_t salt_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/strand_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/strand_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_STRAND_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/string_view.hpp b/subprojects/asio/include/asio/detail/string_view.hpp new file mode 100644 index 000000000..f09cebc2b --- /dev/null +++ b/subprojects/asio/include/asio/detail/string_view.hpp @@ -0,0 +1,47 @@ +// +// detail/string_view.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STRING_VIEW_HPP +#define ASIO_DETAIL_STRING_VIEW_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STRING_VIEW) + +#if defined(ASIO_HAS_STD_STRING_VIEW) +# include +#elif defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# include +#else // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# error ASIO_HAS_STRING_VIEW is set but no string_view is available +#endif // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) + +namespace asio { + +#if defined(ASIO_HAS_STD_STRING_VIEW) +using std::basic_string_view; +using std::string_view; +#elif defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +using std::experimental::basic_string_view; +using std::experimental::string_view; +#endif // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) + +} // namespace asio + +# define ASIO_STRING_VIEW_PARAM asio::string_view +#else // defined(ASIO_HAS_STRING_VIEW) +# define ASIO_STRING_VIEW_PARAM const std::string& +#endif // defined(ASIO_HAS_STRING_VIEW) + +#endif // ASIO_DETAIL_STRING_VIEW_HPP diff --git a/subprojects/asio/include/asio/detail/thread.hpp b/subprojects/asio/include/asio/detail/thread.hpp new file mode 100644 index 000000000..ea556dba6 --- /dev/null +++ b/subprojects/asio/include/asio/detail/thread.hpp @@ -0,0 +1,60 @@ +// +// detail/thread.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_HPP +#define ASIO_DETAIL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_thread.hpp" +#elif defined(ASIO_WINDOWS) +# if defined(UNDER_CE) +# include "asio/detail/wince_thread.hpp" +# elif defined(ASIO_WINDOWS_APP) +# include "asio/detail/winapp_thread.hpp" +# else +# include "asio/detail/win_thread.hpp" +# endif +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_thread.hpp" +#elif defined(ASIO_HAS_STD_THREAD) +# include "asio/detail/std_thread.hpp" +#else +# error Only Windows, POSIX and std::thread are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_thread thread; +#elif defined(ASIO_WINDOWS) +# if defined(UNDER_CE) +typedef wince_thread thread; +# elif defined(ASIO_WINDOWS_APP) +typedef winapp_thread thread; +# else +typedef win_thread thread; +# endif +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_thread thread; +#elif defined(ASIO_HAS_STD_THREAD) +typedef std_thread thread; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_THREAD_HPP diff --git a/subprojects/asio/include/asio/detail/thread_context.hpp b/subprojects/asio/include/asio/detail/thread_context.hpp new file mode 100644 index 000000000..88b4f3115 --- /dev/null +++ b/subprojects/asio/include/asio/detail/thread_context.hpp @@ -0,0 +1,42 @@ +// +// detail/thread_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_CONTEXT_HPP +#define ASIO_DETAIL_THREAD_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include "asio/detail/call_stack.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class thread_info_base; + +// Base class for things that manage threads (scheduler, win_iocp_io_context). +class thread_context +{ +public: + // Per-thread call stack to track the state of each thread in the context. + typedef call_stack thread_call_stack; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THREAD_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/detail/thread_group.hpp b/subprojects/asio/include/asio/detail/thread_group.hpp new file mode 100644 index 000000000..1e400b037 --- /dev/null +++ b/subprojects/asio/include/asio/detail/thread_group.hpp @@ -0,0 +1,89 @@ +// +// detail/thread_group.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_GROUP_HPP +#define ASIO_DETAIL_THREAD_GROUP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/thread.hpp" + +namespace asio { +namespace detail { + +class thread_group +{ +public: + // Constructor initialises an empty thread group. + thread_group() + : first_(0) + { + } + + // Destructor joins any remaining threads in the group. + ~thread_group() + { + join(); + } + + // Create a new thread in the group. + template + void create_thread(Function f) + { + first_ = new item(f, first_); + } + + // Create new threads in the group. + template + void create_threads(Function f, std::size_t num_threads) + { + for (std::size_t i = 0; i < num_threads; ++i) + create_thread(f); + } + + // Wait for all threads in the group to exit. + void join() + { + while (first_) + { + first_->thread_.join(); + item* tmp = first_; + first_ = first_->next_; + delete tmp; + } + } + +private: + // Structure used to track a single thread in the group. + struct item + { + template + explicit item(Function f, item* next) + : thread_(f), + next_(next) + { + } + + asio::detail::thread thread_; + item* next_; + }; + + // The first thread in the group. + item* first_; +}; + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_THREAD_GROUP_HPP diff --git a/subprojects/asio/include/asio/detail/thread_info_base.hpp b/subprojects/asio/include/asio/detail/thread_info_base.hpp new file mode 100644 index 000000000..1b2220745 --- /dev/null +++ b/subprojects/asio/include/asio/detail/thread_info_base.hpp @@ -0,0 +1,121 @@ +// +// detail/thread_info_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_INFO_BASE_HPP +#define ASIO_DETAIL_THREAD_INFO_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class thread_info_base + : private noncopyable +{ +public: + struct default_tag + { + enum { mem_index = 0 }; + }; + + struct awaitee_tag + { + enum { mem_index = 1 }; + }; + + thread_info_base() + { + for (int i = 0; i < max_mem_index; ++i) + reusable_memory_[i] = 0; + } + + ~thread_info_base() + { + for (int i = 0; i < max_mem_index; ++i) + if (reusable_memory_[i]) + ::operator delete(reusable_memory_[i]); + } + + static void* allocate(thread_info_base* this_thread, std::size_t size) + { + return allocate(default_tag(), this_thread, size); + } + + static void deallocate(thread_info_base* this_thread, + void* pointer, std::size_t size) + { + deallocate(default_tag(), this_thread, pointer, size); + } + + template + static void* allocate(Purpose, thread_info_base* this_thread, + std::size_t size) + { + std::size_t chunks = (size + chunk_size - 1) / chunk_size; + + if (this_thread && this_thread->reusable_memory_[Purpose::mem_index]) + { + void* const pointer = this_thread->reusable_memory_[Purpose::mem_index]; + this_thread->reusable_memory_[Purpose::mem_index] = 0; + + unsigned char* const mem = static_cast(pointer); + if (static_cast(mem[0]) >= chunks) + { + mem[size] = mem[0]; + return pointer; + } + + ::operator delete(pointer); + } + + void* const pointer = ::operator new(chunks * chunk_size + 1); + unsigned char* const mem = static_cast(pointer); + mem[size] = (chunks <= UCHAR_MAX) ? static_cast(chunks) : 0; + return pointer; + } + + template + static void deallocate(Purpose, thread_info_base* this_thread, + void* pointer, std::size_t size) + { + if (size <= chunk_size * UCHAR_MAX) + { + if (this_thread && this_thread->reusable_memory_[Purpose::mem_index] == 0) + { + unsigned char* const mem = static_cast(pointer); + mem[0] = mem[size]; + this_thread->reusable_memory_[Purpose::mem_index] = pointer; + return; + } + } + + ::operator delete(pointer); + } + +private: + enum { chunk_size = 4 }; + enum { max_mem_index = 2 }; + void* reusable_memory_[max_mem_index]; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THREAD_INFO_BASE_HPP diff --git a/subprojects/asio/include/asio/detail/throw_error.hpp b/subprojects/asio/include/asio/detail/throw_error.hpp new file mode 100644 index 000000000..5dd87855a --- /dev/null +++ b/subprojects/asio/include/asio/detail/throw_error.hpp @@ -0,0 +1,53 @@ +// +// detail/throw_error.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THROW_ERROR_HPP +#define ASIO_DETAIL_THROW_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +ASIO_DECL void do_throw_error(const asio::error_code& err); + +ASIO_DECL void do_throw_error(const asio::error_code& err, + const char* location); + +inline void throw_error(const asio::error_code& err) +{ + if (err) + do_throw_error(err); +} + +inline void throw_error(const asio::error_code& err, + const char* location) +{ + if (err) + do_throw_error(err, location); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/throw_error.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_THROW_ERROR_HPP diff --git a/subprojects/asio/include/asio/detail/throw_exception.hpp b/subprojects/asio/include/asio/detail/throw_exception.hpp new file mode 100644 index 000000000..f9f7bfb9f --- /dev/null +++ b/subprojects/asio/include/asio/detail/throw_exception.hpp @@ -0,0 +1,51 @@ +// +// detail/throw_exception.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THROW_EXCEPTION_HPP +#define ASIO_DETAIL_THROW_EXCEPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_THROW_EXCEPTION) +# include +#endif // defined(ASIO_BOOST_THROW_EXCEPTION) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_BOOST_THROW_EXCEPTION) +using boost::throw_exception; +#else // defined(ASIO_HAS_BOOST_THROW_EXCEPTION) + +// Declare the throw_exception function for all targets. +template +void throw_exception(const Exception& e); + +// Only define the throw_exception function when exceptions are enabled. +// Otherwise, it is up to the application to provide a definition of this +// function. +# if !defined(ASIO_NO_EXCEPTIONS) +template +void throw_exception(const Exception& e) +{ + throw e; +} +# endif // !defined(ASIO_NO_EXCEPTIONS) + +#endif // defined(ASIO_HAS_BOOST_THROW_EXCEPTION) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_THROW_EXCEPTION_HPP diff --git a/subprojects/asio/include/asio/detail/timer_queue.hpp b/subprojects/asio/include/asio/detail/timer_queue.hpp new file mode 100644 index 000000000..de37000db --- /dev/null +++ b/subprojects/asio/include/asio/detail/timer_queue.hpp @@ -0,0 +1,360 @@ +// +// detail/timer_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/cstdint.hpp" +#include "asio/detail/date_time_fwd.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class timer_queue + : public timer_queue_base +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // Per-timer data. + class per_timer_data + { + public: + per_timer_data() : + heap_index_((std::numeric_limits::max)()), + next_(0), prev_(0) + { + } + + private: + friend class timer_queue; + + // The operations waiting on the timer. + op_queue op_queue_; + + // The index of the timer in the heap. + std::size_t heap_index_; + + // Pointers to adjacent timers in a linked list. + per_timer_data* next_; + per_timer_data* prev_; + }; + + // Constructor. + timer_queue() + : timers_(), + heap_() + { + } + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + bool enqueue_timer(const time_type& time, per_timer_data& timer, wait_op* op) + { + // Enqueue the timer object. + if (timer.prev_ == 0 && &timer != timers_) + { + if (this->is_positive_infinity(time)) + { + // No heap entry is required for timers that never expire. + timer.heap_index_ = (std::numeric_limits::max)(); + } + else + { + // Put the new timer at the correct position in the heap. This is done + // first since push_back() can throw due to allocation failure. + timer.heap_index_ = heap_.size(); + heap_entry entry = { time, &timer }; + heap_.push_back(entry); + up_heap(heap_.size() - 1); + } + + // Insert the new timer into the linked list of active timers. + timer.next_ = timers_; + timer.prev_ = 0; + if (timers_) + timers_->prev_ = &timer; + timers_ = &timer; + } + + // Enqueue the individual timer operation. + timer.op_queue_.push(op); + + // Interrupt reactor only if newly added timer is first to expire. + return timer.heap_index_ == 0 && timer.op_queue_.front() == op; + } + + // Whether there are no timers in the queue. + virtual bool empty() const + { + return timers_ == 0; + } + + // Get the time for the timer that is earliest in the queue. + virtual long wait_duration_msec(long max_duration) const + { + if (heap_.empty()) + return max_duration; + + return this->to_msec( + Time_Traits::to_posix_duration( + Time_Traits::subtract(heap_[0].time_, Time_Traits::now())), + max_duration); + } + + // Get the time for the timer that is earliest in the queue. + virtual long wait_duration_usec(long max_duration) const + { + if (heap_.empty()) + return max_duration; + + return this->to_usec( + Time_Traits::to_posix_duration( + Time_Traits::subtract(heap_[0].time_, Time_Traits::now())), + max_duration); + } + + // Dequeue all timers not later than the current time. + virtual void get_ready_timers(op_queue& ops) + { + if (!heap_.empty()) + { + const time_type now = Time_Traits::now(); + while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0].time_)) + { + per_timer_data* timer = heap_[0].timer_; + ops.push(timer->op_queue_); + remove_timer(*timer); + } + } + } + + // Dequeue all timers. + virtual void get_all_timers(op_queue& ops) + { + while (timers_) + { + per_timer_data* timer = timers_; + timers_ = timers_->next_; + ops.push(timer->op_queue_); + timer->next_ = 0; + timer->prev_ = 0; + } + + heap_.clear(); + } + + // Cancel and dequeue operations for the given timer. + std::size_t cancel_timer(per_timer_data& timer, op_queue& ops, + std::size_t max_cancelled = (std::numeric_limits::max)()) + { + std::size_t num_cancelled = 0; + if (timer.prev_ != 0 || &timer == timers_) + { + while (wait_op* op = (num_cancelled != max_cancelled) + ? timer.op_queue_.front() : 0) + { + op->ec_ = asio::error::operation_aborted; + timer.op_queue_.pop(); + ops.push(op); + ++num_cancelled; + } + if (timer.op_queue_.empty()) + remove_timer(timer); + } + return num_cancelled; + } + + // Move operations from one timer to another, empty timer. + void move_timer(per_timer_data& target, per_timer_data& source) + { + target.op_queue_.push(source.op_queue_); + + target.heap_index_ = source.heap_index_; + source.heap_index_ = (std::numeric_limits::max)(); + + if (target.heap_index_ < heap_.size()) + heap_[target.heap_index_].timer_ = ⌖ + + if (timers_ == &source) + timers_ = ⌖ + if (source.prev_) + source.prev_->next_ = ⌖ + if (source.next_) + source.next_->prev_= ⌖ + target.next_ = source.next_; + target.prev_ = source.prev_; + source.next_ = 0; + source.prev_ = 0; + } + +private: + // Move the item at the given index up the heap to its correct position. + void up_heap(std::size_t index) + { + while (index > 0) + { + std::size_t parent = (index - 1) / 2; + if (!Time_Traits::less_than(heap_[index].time_, heap_[parent].time_)) + break; + swap_heap(index, parent); + index = parent; + } + } + + // Move the item at the given index down the heap to its correct position. + void down_heap(std::size_t index) + { + std::size_t child = index * 2 + 1; + while (child < heap_.size()) + { + std::size_t min_child = (child + 1 == heap_.size() + || Time_Traits::less_than( + heap_[child].time_, heap_[child + 1].time_)) + ? child : child + 1; + if (Time_Traits::less_than(heap_[index].time_, heap_[min_child].time_)) + break; + swap_heap(index, min_child); + index = min_child; + child = index * 2 + 1; + } + } + + // Swap two entries in the heap. + void swap_heap(std::size_t index1, std::size_t index2) + { + heap_entry tmp = heap_[index1]; + heap_[index1] = heap_[index2]; + heap_[index2] = tmp; + heap_[index1].timer_->heap_index_ = index1; + heap_[index2].timer_->heap_index_ = index2; + } + + // Remove a timer from the heap and list of timers. + void remove_timer(per_timer_data& timer) + { + // Remove the timer from the heap. + std::size_t index = timer.heap_index_; + if (!heap_.empty() && index < heap_.size()) + { + if (index == heap_.size() - 1) + { + timer.heap_index_ = (std::numeric_limits::max)(); + heap_.pop_back(); + } + else + { + swap_heap(index, heap_.size() - 1); + timer.heap_index_ = (std::numeric_limits::max)(); + heap_.pop_back(); + if (index > 0 && Time_Traits::less_than( + heap_[index].time_, heap_[(index - 1) / 2].time_)) + up_heap(index); + else + down_heap(index); + } + } + + // Remove the timer from the linked list of active timers. + if (timers_ == &timer) + timers_ = timer.next_; + if (timer.prev_) + timer.prev_->next_ = timer.next_; + if (timer.next_) + timer.next_->prev_= timer.prev_; + timer.next_ = 0; + timer.prev_ = 0; + } + + // Determine if the specified absolute time is positive infinity. + template + static bool is_positive_infinity(const Time_Type&) + { + return false; + } + + // Determine if the specified absolute time is positive infinity. + template + static bool is_positive_infinity( + const boost::date_time::base_time& time) + { + return time.is_pos_infinity(); + } + + // Helper function to convert a duration into milliseconds. + template + long to_msec(const Duration& d, long max_duration) const + { + if (d.ticks() <= 0) + return 0; + int64_t msec = d.total_milliseconds(); + if (msec == 0) + return 1; + if (msec > max_duration) + return max_duration; + return static_cast(msec); + } + + // Helper function to convert a duration into microseconds. + template + long to_usec(const Duration& d, long max_duration) const + { + if (d.ticks() <= 0) + return 0; + int64_t usec = d.total_microseconds(); + if (usec == 0) + return 1; + if (usec > max_duration) + return max_duration; + return static_cast(usec); + } + + // The head of a linked list of all active timers. + per_timer_data* timers_; + + struct heap_entry + { + // The time when the timer should fire. + time_type time_; + + // The associated timer with enqueued operations. + per_timer_data* timer_; + }; + + // The heap of timers, with the earliest timer at the front. + std::vector heap_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_HPP diff --git a/subprojects/asio/include/asio/detail/timer_queue_base.hpp b/subprojects/asio/include/asio/detail/timer_queue_base.hpp new file mode 100644 index 000000000..4af995f23 --- /dev/null +++ b/subprojects/asio/include/asio/detail/timer_queue_base.hpp @@ -0,0 +1,68 @@ +// +// detail/timer_queue_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_BASE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class timer_queue_base + : private noncopyable +{ +public: + // Constructor. + timer_queue_base() : next_(0) {} + + // Destructor. + virtual ~timer_queue_base() {} + + // Whether there are no timers in the queue. + virtual bool empty() const = 0; + + // Get the time to wait until the next timer. + virtual long wait_duration_msec(long max_duration) const = 0; + + // Get the time to wait until the next timer. + virtual long wait_duration_usec(long max_duration) const = 0; + + // Dequeue all ready timers. + virtual void get_ready_timers(op_queue& ops) = 0; + + // Dequeue all timers. + virtual void get_all_timers(op_queue& ops) = 0; + +private: + friend class timer_queue_set; + + // Next timer queue in the set. + timer_queue_base* next_; +}; + +template +class timer_queue; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_BASE_HPP diff --git a/subprojects/asio/include/asio/detail/timer_queue_ptime.hpp b/subprojects/asio/include/asio/detail/timer_queue_ptime.hpp new file mode 100644 index 000000000..84e83385e --- /dev/null +++ b/subprojects/asio/include/asio/detail/timer_queue_ptime.hpp @@ -0,0 +1,99 @@ +// +// detail/timer_queue_ptime.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_PTIME_HPP +#define ASIO_DETAIL_TIMER_QUEUE_PTIME_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) + +#include "asio/time_traits.hpp" +#include "asio/detail/timer_queue.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct forwarding_posix_time_traits : time_traits {}; + +// Template specialisation for the commonly used instantation. +template <> +class timer_queue > + : public timer_queue_base +{ +public: + // The time type. + typedef boost::posix_time::ptime time_type; + + // The duration type. + typedef boost::posix_time::time_duration duration_type; + + // Per-timer data. + typedef timer_queue::per_timer_data + per_timer_data; + + // Constructor. + ASIO_DECL timer_queue(); + + // Destructor. + ASIO_DECL virtual ~timer_queue(); + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + ASIO_DECL bool enqueue_timer(const time_type& time, + per_timer_data& timer, wait_op* op); + + // Whether there are no timers in the queue. + ASIO_DECL virtual bool empty() const; + + // Get the time for the timer that is earliest in the queue. + ASIO_DECL virtual long wait_duration_msec(long max_duration) const; + + // Get the time for the timer that is earliest in the queue. + ASIO_DECL virtual long wait_duration_usec(long max_duration) const; + + // Dequeue all timers not later than the current time. + ASIO_DECL virtual void get_ready_timers(op_queue& ops); + + // Dequeue all timers. + ASIO_DECL virtual void get_all_timers(op_queue& ops); + + // Cancel and dequeue operations for the given timer. + ASIO_DECL std::size_t cancel_timer( + per_timer_data& timer, op_queue& ops, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move operations from one timer to another, empty timer. + ASIO_DECL void move_timer(per_timer_data& target, + per_timer_data& source); + +private: + timer_queue impl_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/timer_queue_ptime.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#endif // ASIO_DETAIL_TIMER_QUEUE_PTIME_HPP diff --git a/subprojects/asio/include/asio/detail/timer_queue_set.hpp b/subprojects/asio/include/asio/detail/timer_queue_set.hpp new file mode 100644 index 000000000..0ea372ae5 --- /dev/null +++ b/subprojects/asio/include/asio/detail/timer_queue_set.hpp @@ -0,0 +1,66 @@ +// +// detail/timer_queue_set.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_SET_HPP +#define ASIO_DETAIL_TIMER_QUEUE_SET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/timer_queue_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class timer_queue_set +{ +public: + // Constructor. + ASIO_DECL timer_queue_set(); + + // Add a timer queue to the set. + ASIO_DECL void insert(timer_queue_base* q); + + // Remove a timer queue from the set. + ASIO_DECL void erase(timer_queue_base* q); + + // Determine whether all queues are empty. + ASIO_DECL bool all_empty() const; + + // Get the wait duration in milliseconds. + ASIO_DECL long wait_duration_msec(long max_duration) const; + + // Get the wait duration in microseconds. + ASIO_DECL long wait_duration_usec(long max_duration) const; + + // Dequeue all ready timers. + ASIO_DECL void get_ready_timers(op_queue& ops); + + // Dequeue all timers. + ASIO_DECL void get_all_timers(op_queue& ops); + +private: + timer_queue_base* first_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/timer_queue_set.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_TIMER_QUEUE_SET_HPP diff --git a/subprojects/asio/include/asio/detail/timer_scheduler.hpp b/subprojects/asio/include/asio/detail/timer_scheduler.hpp new file mode 100644 index 000000000..b1f8df45d --- /dev/null +++ b/subprojects/asio/include/asio/detail/timer_scheduler.hpp @@ -0,0 +1,35 @@ +// +// detail/timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_SCHEDULER_HPP +#define ASIO_DETAIL_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/timer_scheduler_fwd.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_timer_scheduler.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#elif defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor.hpp" +#else +# include "asio/detail/select_reactor.hpp" +#endif + +#endif // ASIO_DETAIL_TIMER_SCHEDULER_HPP diff --git a/subprojects/asio/include/asio/detail/timer_scheduler_fwd.hpp b/subprojects/asio/include/asio/detail/timer_scheduler_fwd.hpp new file mode 100644 index 000000000..80bae5034 --- /dev/null +++ b/subprojects/asio/include/asio/detail/timer_scheduler_fwd.hpp @@ -0,0 +1,40 @@ +// +// detail/timer_scheduler_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP +#define ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS_RUNTIME) +typedef class winrt_timer_scheduler timer_scheduler; +#elif defined(ASIO_HAS_IOCP) +typedef class win_iocp_io_context timer_scheduler; +#elif defined(ASIO_HAS_EPOLL) +typedef class epoll_reactor timer_scheduler; +#elif defined(ASIO_HAS_KQUEUE) +typedef class kqueue_reactor timer_scheduler; +#elif defined(ASIO_HAS_DEV_POLL) +typedef class dev_poll_reactor timer_scheduler; +#else +typedef class select_reactor timer_scheduler; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP diff --git a/subprojects/asio/include/asio/detail/tss_ptr.hpp b/subprojects/asio/include/asio/detail/tss_ptr.hpp new file mode 100644 index 000000000..e628abe9d --- /dev/null +++ b/subprojects/asio/include/asio/detail/tss_ptr.hpp @@ -0,0 +1,69 @@ +// +// detail/tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TSS_PTR_HPP +#define ASIO_DETAIL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_tss_ptr.hpp" +#elif defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) +# include "asio/detail/keyword_tss_ptr.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_tss_ptr.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_tss_ptr.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class tss_ptr +#if !defined(ASIO_HAS_THREADS) + : public null_tss_ptr +#elif defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + : public keyword_tss_ptr +#elif defined(ASIO_WINDOWS) + : public win_tss_ptr +#elif defined(ASIO_HAS_PTHREADS) + : public posix_tss_ptr +#endif +{ +public: + void operator=(T* value) + { +#if !defined(ASIO_HAS_THREADS) + null_tss_ptr::operator=(value); +#elif defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + keyword_tss_ptr::operator=(value); +#elif defined(ASIO_WINDOWS) + win_tss_ptr::operator=(value); +#elif defined(ASIO_HAS_PTHREADS) + posix_tss_ptr::operator=(value); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TSS_PTR_HPP diff --git a/subprojects/asio/include/asio/detail/type_traits.hpp b/subprojects/asio/include/asio/detail/type_traits.hpp new file mode 100644 index 000000000..edf09281a --- /dev/null +++ b/subprojects/asio/include/asio/detail/type_traits.hpp @@ -0,0 +1,86 @@ +// +// detail/type_traits.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TYPE_TRAITS_HPP +#define ASIO_DETAIL_TYPE_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_TYPE_TRAITS) +# include +#else // defined(ASIO_HAS_TYPE_TRAITS) +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ASIO_HAS_TYPE_TRAITS) + +namespace asio { + +#if defined(ASIO_HAS_STD_TYPE_TRAITS) +using std::add_const; +using std::conditional; +using std::decay; +using std::enable_if; +using std::false_type; +using std::integral_constant; +using std::is_base_of; +using std::is_class; +using std::is_const; +using std::is_convertible; +using std::is_function; +using std::is_same; +using std::remove_pointer; +using std::remove_reference; +#if defined(ASIO_HAS_STD_INVOKE_RESULT) +template struct result_of; +template +struct result_of : std::invoke_result {}; +#else // defined(ASIO_HAS_STD_INVOKE_RESULT) +using std::result_of; +#endif // defined(ASIO_HAS_STD_INVOKE_RESULT) +using std::true_type; +#else // defined(ASIO_HAS_STD_TYPE_TRAITS) +using boost::add_const; +template +struct enable_if : boost::enable_if_c {}; +using boost::conditional; +using boost::decay; +using boost::false_type; +using boost::integral_constant; +using boost::is_base_of; +using boost::is_class; +using boost::is_const; +using boost::is_convertible; +using boost::is_function; +using boost::is_same; +using boost::remove_pointer; +using boost::remove_reference; +using boost::result_of; +using boost::true_type; +#endif // defined(ASIO_HAS_STD_TYPE_TRAITS) + +} // namespace asio + +#endif // ASIO_DETAIL_TYPE_TRAITS_HPP diff --git a/subprojects/asio/include/asio/detail/variadic_templates.hpp b/subprojects/asio/include/asio/detail/variadic_templates.hpp new file mode 100644 index 000000000..d54eb4e4b --- /dev/null +++ b/subprojects/asio/include/asio/detail/variadic_templates.hpp @@ -0,0 +1,119 @@ +// +// detail/variadic_templates.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_VARIADIC_TEMPLATES_HPP +#define ASIO_DETAIL_VARIADIC_TEMPLATES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +# define ASIO_VARIADIC_TPARAMS(n) ASIO_VARIADIC_TPARAMS_##n + +# define ASIO_VARIADIC_TPARAMS_1 \ + typename T1 +# define ASIO_VARIADIC_TPARAMS_2 \ + typename T1, typename T2 +# define ASIO_VARIADIC_TPARAMS_3 \ + typename T1, typename T2, typename T3 +# define ASIO_VARIADIC_TPARAMS_4 \ + typename T1, typename T2, typename T3, typename T4 +# define ASIO_VARIADIC_TPARAMS_5 \ + typename T1, typename T2, typename T3, typename T4, typename T5 + +# define ASIO_VARIADIC_TARGS(n) ASIO_VARIADIC_TARGS_##n + +# define ASIO_VARIADIC_TARGS_1 T1 +# define ASIO_VARIADIC_TARGS_2 T1, T2 +# define ASIO_VARIADIC_TARGS_3 T1, T2, T3 +# define ASIO_VARIADIC_TARGS_4 T1, T2, T3, T4 +# define ASIO_VARIADIC_TARGS_5 T1, T2, T3, T4, T5 + +# define ASIO_VARIADIC_BYVAL_PARAMS(n) \ + ASIO_VARIADIC_BYVAL_PARAMS_##n + +# define ASIO_VARIADIC_BYVAL_PARAMS_1 T1 x1 +# define ASIO_VARIADIC_BYVAL_PARAMS_2 T1 x1, T2 x2 +# define ASIO_VARIADIC_BYVAL_PARAMS_3 T1 x1, T2 x2, T3 x3 +# define ASIO_VARIADIC_BYVAL_PARAMS_4 T1 x1, T2 x2, T3 x3, T4 x4 +# define ASIO_VARIADIC_BYVAL_PARAMS_5 T1 x1, T2 x2, T3 x3, T4 x4, T5 x5 + +# define ASIO_VARIADIC_BYVAL_ARGS(n) \ + ASIO_VARIADIC_BYVAL_ARGS_##n + +# define ASIO_VARIADIC_BYVAL_ARGS_1 x1 +# define ASIO_VARIADIC_BYVAL_ARGS_2 x1, x2 +# define ASIO_VARIADIC_BYVAL_ARGS_3 x1, x2, x3 +# define ASIO_VARIADIC_BYVAL_ARGS_4 x1, x2, x3, x4 +# define ASIO_VARIADIC_BYVAL_ARGS_5 x1, x2, x3, x4, x5 + +# define ASIO_VARIADIC_MOVE_PARAMS(n) \ + ASIO_VARIADIC_MOVE_PARAMS_##n + +# define ASIO_VARIADIC_MOVE_PARAMS_1 \ + ASIO_MOVE_ARG(T1) x1 +# define ASIO_VARIADIC_MOVE_PARAMS_2 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2 +# define ASIO_VARIADIC_MOVE_PARAMS_3 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3 +# define ASIO_VARIADIC_MOVE_PARAMS_4 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3, ASIO_MOVE_ARG(T4) x4 +# define ASIO_VARIADIC_MOVE_PARAMS_5 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3, ASIO_MOVE_ARG(T4) x4, \ + ASIO_MOVE_ARG(T5) x5 + +# define ASIO_VARIADIC_MOVE_ARGS(n) \ + ASIO_VARIADIC_MOVE_ARGS_##n + +# define ASIO_VARIADIC_MOVE_ARGS_1 \ + ASIO_MOVE_CAST(T1)(x1) +# define ASIO_VARIADIC_MOVE_ARGS_2 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2) +# define ASIO_VARIADIC_MOVE_ARGS_3 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3) +# define ASIO_VARIADIC_MOVE_ARGS_4 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3), ASIO_MOVE_CAST(T4)(x4) +# define ASIO_VARIADIC_MOVE_ARGS_5 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3), ASIO_MOVE_CAST(T4)(x4), \ + ASIO_MOVE_CAST(T5)(x5) + +# define ASIO_VARIADIC_DECAY(n) \ + ASIO_VARIADIC_DECAY_##n + +# define ASIO_VARIADIC_DECAY_1 \ + typename decay::type +# define ASIO_VARIADIC_DECAY_2 \ + typename decay::type, typename decay::type +# define ASIO_VARIADIC_DECAY_3 \ + typename decay::type, typename decay::type, \ + typename decay::type +# define ASIO_VARIADIC_DECAY_4 \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type +# define ASIO_VARIADIC_DECAY_5 \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type, \ + typename decay::type + +# define ASIO_VARIADIC_GENERATE(m) m(1) m(2) m(3) m(4) m(5) + +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // ASIO_DETAIL_VARIADIC_TEMPLATES_HPP diff --git a/subprojects/asio/include/asio/detail/wait_handler.hpp b/subprojects/asio/include/asio/detail/wait_handler.hpp new file mode 100644 index 000000000..bd3dc2441 --- /dev/null +++ b/subprojects/asio/include/asio/detail/wait_handler.hpp @@ -0,0 +1,85 @@ +// +// detail/wait_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WAIT_HANDLER_HPP +#define ASIO_DETAIL_WAIT_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class wait_handler : public wait_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(wait_handler); + + wait_handler(Handler& h) + : wait_op(&wait_handler::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(h)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + wait_handler* h(static_cast(base)); + ptr p = { asio::detail::addressof(h->handler_), h, h }; + handler_work w(h->handler_); + + ASIO_HANDLER_COMPLETION((*h)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(h->handler_, h->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WAIT_HANDLER_HPP diff --git a/subprojects/asio/include/asio/detail/wait_op.hpp b/subprojects/asio/include/asio/detail/wait_op.hpp new file mode 100644 index 000000000..1a3017bae --- /dev/null +++ b/subprojects/asio/include/asio/detail/wait_op.hpp @@ -0,0 +1,45 @@ +// +// detail/wait_op.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WAIT_OP_HPP +#define ASIO_DETAIL_WAIT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class wait_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + +protected: + wait_op(func_type func) + : operation(func) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WAIT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_event.hpp b/subprojects/asio/include/asio/detail/win_event.hpp new file mode 100644 index 000000000..859cdee7b --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_event.hpp @@ -0,0 +1,151 @@ +// +// detail/win_event.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_EVENT_HPP +#define ASIO_DETAIL_WIN_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_event + : private noncopyable +{ +public: + // Constructor. + ASIO_DECL win_event(); + + // Destructor. + ASIO_DECL ~win_event(); + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock& lock) + { + this->signal_all(lock); + } + + // Signal all waiters. + template + void signal_all(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ |= 1; + ::SetEvent(events_[0]); + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + lock.unlock(); + if (have_waiters) + ::SetEvent(events_[1]); + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + if (state_ > 1) + { + lock.unlock(); + ::SetEvent(events_[1]); + return true; + } + return false; + } + + // Reset the event. + template + void clear(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + ::ResetEvent(events_[0]); + state_ &= ~std::size_t(1); + } + + // Wait for the event to become signalled. + template + void wait(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + while ((state_ & 1) == 0) + { + state_ += 2; + lock.unlock(); +#if defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjectsEx(2, events_, false, INFINITE, false); +#else // defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjects(2, events_, false, INFINITE); +#endif // defined(ASIO_WINDOWS_APP) + lock.lock(); + state_ -= 2; + } + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock& lock, long usec) + { + ASIO_ASSERT(lock.locked()); + if ((state_ & 1) == 0) + { + state_ += 2; + lock.unlock(); + DWORD msec = usec > 0 ? (usec < 1000 ? 1 : usec / 1000) : 0; +#if defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjectsEx(2, events_, false, msec, false); +#else // defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjects(2, events_, false, msec); +#endif // defined(ASIO_WINDOWS_APP) + lock.lock(); + state_ -= 2; + } + return (state_ & 1) != 0; + } + +private: + HANDLE events_[2]; + std::size_t state_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_EVENT_HPP diff --git a/subprojects/asio/include/asio/detail/win_fd_set_adapter.hpp b/subprojects/asio/include/asio/detail/win_fd_set_adapter.hpp new file mode 100644 index 000000000..8d5e700e2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_fd_set_adapter.hpp @@ -0,0 +1,149 @@ +// +// detail/win_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class win_fd_set_adapter : noncopyable +{ +public: + enum { default_fd_set_size = 1024 }; + + win_fd_set_adapter() + : capacity_(default_fd_set_size), + max_descriptor_(invalid_socket) + { + fd_set_ = static_cast(::operator new( + sizeof(win_fd_set) - sizeof(SOCKET) + + sizeof(SOCKET) * (capacity_))); + fd_set_->fd_count = 0; + } + + ~win_fd_set_adapter() + { + ::operator delete(fd_set_); + } + + void reset() + { + fd_set_->fd_count = 0; + max_descriptor_ = invalid_socket; + } + + bool set(socket_type descriptor) + { + for (u_int i = 0; i < fd_set_->fd_count; ++i) + if (fd_set_->fd_array[i] == descriptor) + return true; + + reserve(fd_set_->fd_count + 1); + fd_set_->fd_array[fd_set_->fd_count++] = descriptor; + return true; + } + + void set(reactor_op_queue& operations, op_queue&) + { + reactor_op_queue::iterator i = operations.begin(); + while (i != operations.end()) + { + reactor_op_queue::iterator op_iter = i++; + reserve(fd_set_->fd_count + 1); + fd_set_->fd_array[fd_set_->fd_count++] = op_iter->first; + } + } + + bool is_set(socket_type descriptor) const + { + return !!__WSAFDIsSet(descriptor, + const_cast(reinterpret_cast(fd_set_))); + } + + operator fd_set*() + { + return reinterpret_cast(fd_set_); + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + + void perform(reactor_op_queue& operations, + op_queue& ops) const + { + for (u_int i = 0; i < fd_set_->fd_count; ++i) + operations.perform_operations(fd_set_->fd_array[i], ops); + } + +private: + // This structure is defined to be compatible with the Windows API fd_set + // structure, but without being dependent on the value of FD_SETSIZE. We use + // the "struct hack" to allow the number of descriptors to be varied at + // runtime. + struct win_fd_set + { + u_int fd_count; + SOCKET fd_array[1]; + }; + + // Increase the fd_set_ capacity to at least the specified number of elements. + void reserve(u_int n) + { + if (n <= capacity_) + return; + + u_int new_capacity = capacity_ + capacity_ / 2; + if (new_capacity < n) + new_capacity = n; + + win_fd_set* new_fd_set = static_cast(::operator new( + sizeof(win_fd_set) - sizeof(SOCKET) + + sizeof(SOCKET) * (new_capacity))); + + new_fd_set->fd_count = fd_set_->fd_count; + for (u_int i = 0; i < fd_set_->fd_count; ++i) + new_fd_set->fd_array[i] = fd_set_->fd_array[i]; + + ::operator delete(fd_set_); + fd_set_ = new_fd_set; + capacity_ = new_capacity; + } + + win_fd_set* fd_set_; + u_int capacity_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP diff --git a/subprojects/asio/include/asio/detail/win_fenced_block.hpp b/subprojects/asio/include/asio/detail/win_fenced_block.hpp new file mode 100644 index 000000000..1ce6e13b9 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_fenced_block.hpp @@ -0,0 +1,90 @@ +// +// detail/win_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_FENCED_BLOCK_HPP +#define ASIO_DETAIL_WIN_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) && !defined(UNDER_CE) + +#include "asio/detail/socket_types.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit win_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit win_fenced_block(full_t) + { +#if defined(__BORLANDC__) + LONG barrier = 0; + ::InterlockedExchange(&barrier, 1); +#elif defined(ASIO_MSVC) \ + && ((ASIO_MSVC < 1400) || !defined(MemoryBarrier)) +# if defined(_M_IX86) +# pragma warning(push) +# pragma warning(disable:4793) + LONG barrier; + __asm { xchg barrier, eax } +# pragma warning(pop) +# endif // defined(_M_IX86) +#else + MemoryBarrier(); +#endif + } + + // Destructor. + ~win_fenced_block() + { +#if defined(__BORLANDC__) + LONG barrier = 0; + ::InterlockedExchange(&barrier, 1); +#elif defined(ASIO_MSVC) \ + && ((ASIO_MSVC < 1400) || !defined(MemoryBarrier)) +# if defined(_M_IX86) +# pragma warning(push) +# pragma warning(disable:4793) + LONG barrier; + __asm { xchg barrier, eax } +# pragma warning(pop) +# endif // defined(_M_IX86) +#else + MemoryBarrier(); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) && !defined(UNDER_CE) + +#endif // ASIO_DETAIL_WIN_FENCED_BLOCK_HPP diff --git a/subprojects/asio/include/asio/detail/win_global.hpp b/subprojects/asio/include/asio/detail/win_global.hpp new file mode 100644 index 000000000..dd0a55564 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_global.hpp @@ -0,0 +1,74 @@ +// +// detail/win_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_GLOBAL_HPP +#define ASIO_DETAIL_WIN_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/static_mutex.hpp" +#include "asio/detail/tss_ptr.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct win_global_impl +{ + // Destructor automatically cleans up the global. + ~win_global_impl() + { + delete ptr_; + } + + static win_global_impl instance_; + static static_mutex mutex_; + static T* ptr_; + static tss_ptr tss_ptr_; +}; + +template +win_global_impl win_global_impl::instance_ = { 0 }; + +template +static_mutex win_global_impl::mutex_ = ASIO_STATIC_MUTEX_INIT; + +template +T* win_global_impl::ptr_ = 0; + +template +tss_ptr win_global_impl::tss_ptr_; + +template +T& win_global() +{ + if (static_cast(win_global_impl::tss_ptr_) == 0) + { + win_global_impl::mutex_.init(); + static_mutex::scoped_lock lock(win_global_impl::mutex_); + if (win_global_impl::ptr_ == 0) + win_global_impl::ptr_ = new T; + win_global_impl::tss_ptr_ = win_global_impl::ptr_; + } + + return *win_global_impl::tss_ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_GLOBAL_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_handle_read_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_handle_read_op.hpp new file mode 100644 index 000000000..88e3f6c7b --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_handle_read_op.hpp @@ -0,0 +1,111 @@ +// +// detail/win_iocp_handle_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_handle_read_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_read_op); + + win_iocp_handle_read_op( + const MutableBufferSequence& buffers, Handler& handler) + : operation(&win_iocp_handle_read_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_handle_read_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (owner) + { + // Check whether buffers are still valid. + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_HANDLE_EOF) + ec = asio::error::eof; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_handle_service.hpp b/subprojects/asio/include/asio/detail/win_iocp_handle_service.hpp new file mode 100644 index 000000000..849fe8f18 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_handle_service.hpp @@ -0,0 +1,323 @@ +// +// detail/win_iocp_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/win_iocp_handle_read_op.hpp" +#include "asio/detail/win_iocp_handle_write_op.hpp" +#include "asio/detail/win_iocp_io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_handle_service : + public service_base +{ +public: + // The native type of a stream handle. + typedef HANDLE native_handle_type; + + // The implementation type of the stream handle. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : handle_(INVALID_HANDLE_VALUE), + safe_cancellation_thread_id_(0), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_iocp_handle_service; + + // The native stream handle representation. + native_handle_type handle_; + + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the handle. + DWORD safe_cancellation_thread_id_; + + // Pointers to adjacent handle implementations in linked list. + implementation_type* next_; + implementation_type* prev_; + }; + + ASIO_DECL win_iocp_handle_service(asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new handle implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Move-construct a new handle implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl); + + // Move-assign from another handle implementation. + ASIO_DECL void move_assign(implementation_type& impl, + win_iocp_handle_service& other_service, + implementation_type& other_impl); + + // Destroy a handle implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Assign a native handle to a handle implementation. + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec); + + // Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return impl.handle_ != INVALID_HANDLE_VALUE; + } + + // Destroy a handle implementation. + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); + + // Get the native handle representation. + native_handle_type native_handle(const implementation_type& impl) const + { + return impl.handle_; + } + + // Cancel all operations associated with the handle. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Write the given data. Returns the number of bytes written. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return write_some_at(impl, 0, buffers, ec); + } + + // Write the given data at the specified offset. Returns the number of bytes + // written. + template + size_t write_some_at(implementation_type& impl, uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + asio::const_buffer buffer = + buffer_sequence_adapter::first(buffers); + + return do_write(impl, offset, buffer, ec); + } + + // Start an asynchronous write. The data being written must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_write_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_write_some")); + + start_write_op(impl, 0, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + + // Start an asynchronous write at a specified offset. The data being written + // must be valid for the lifetime of the asynchronous operation. + template + void async_write_some_at(implementation_type& impl, uint64_t offset, + const ConstBufferSequence& buffers, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_write_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_write_some_at")); + + start_write_op(impl, offset, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + + // Read some data. Returns the number of bytes received. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return read_some_at(impl, 0, buffers, ec); + } + + // Read some data at a specified offset. Returns the number of bytes received. + template + size_t read_some_at(implementation_type& impl, uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + asio::mutable_buffer buffer = + buffer_sequence_adapter::first(buffers); + + return do_read(impl, offset, buffer, ec); + } + + // Start an asynchronous read. The buffer for the data being received must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_read_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_read_some")); + + start_read_op(impl, 0, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + + // Start an asynchronous read at a specified offset. The buffer for the data + // being received must be valid for the lifetime of the asynchronous + // operation. + template + void async_read_some_at(implementation_type& impl, uint64_t offset, + const MutableBufferSequence& buffers, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_read_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_read_some_at")); + + start_read_op(impl, offset, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + +private: + // Prevent the use of the null_buffers type with this service. + size_t write_some(implementation_type& impl, + const null_buffers& buffers, asio::error_code& ec); + size_t write_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, asio::error_code& ec); + template + void async_write_some(implementation_type& impl, + const null_buffers& buffers, Handler& handler); + template + void async_write_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, Handler& handler); + size_t read_some(implementation_type& impl, + const null_buffers& buffers, asio::error_code& ec); + size_t read_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, asio::error_code& ec); + template + void async_read_some(implementation_type& impl, + const null_buffers& buffers, Handler& handler); + template + void async_read_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, Handler& handler); + + // Helper class for waiting for synchronous operations to complete. + class overlapped_wrapper; + + // Helper function to perform a synchronous write operation. + ASIO_DECL size_t do_write(implementation_type& impl, + uint64_t offset, const asio::const_buffer& buffer, + asio::error_code& ec); + + // Helper function to start a write operation. + ASIO_DECL void start_write_op(implementation_type& impl, + uint64_t offset, const asio::const_buffer& buffer, + operation* op); + + // Helper function to perform a synchronous write operation. + ASIO_DECL size_t do_read(implementation_type& impl, + uint64_t offset, const asio::mutable_buffer& buffer, + asio::error_code& ec); + + // Helper function to start a read operation. + ASIO_DECL void start_read_op(implementation_type& impl, + uint64_t offset, const asio::mutable_buffer& buffer, + operation* op); + + // Update the ID of the thread from which cancellation is safe. + ASIO_DECL void update_cancellation_thread_id(implementation_type& impl); + + // Helper function to close a handle when the associated object is being + // destroyed. + ASIO_DECL void close_for_destruction(implementation_type& impl); + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_context& iocp_service_; + + // Mutex to protect access to the linked list of implementations. + mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_handle_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_handle_write_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_handle_write_op.hpp new file mode 100644 index 000000000..d7bb9443a --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_handle_write_op.hpp @@ -0,0 +1,103 @@ +// +// detail/win_iocp_handle_write_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_handle_write_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_write_op); + + win_iocp_handle_write_op(const ConstBufferSequence& buffers, Handler& handler) + : operation(&win_iocp_handle_write_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_handle_write_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (owner) + { + // Check whether buffers are still valid. + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + ConstBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_io_context.hpp b/subprojects/asio/include/asio/detail/win_iocp_io_context.hpp new file mode 100644 index 000000000..11bf58b7d --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_io_context.hpp @@ -0,0 +1,328 @@ +// +// detail/win_iocp_io_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/thread_context.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/detail/win_iocp_operation.hpp" +#include "asio/detail/win_iocp_thread_info.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class wait_op; + +class win_iocp_io_context + : public execution_context_service_base, + public thread_context +{ +public: + // Constructor. Specifies a concurrency hint that is passed through to the + // underlying I/O completion port. + ASIO_DECL win_iocp_io_context(asio::execution_context& ctx, + int concurrency_hint = -1); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Initialise the task. Nothing to do here. + void init_task() + { + } + + // Register a handle with the IO completion port. + ASIO_DECL asio::error_code register_handle( + HANDLE handle, asio::error_code& ec); + + // Run the event loop until stopped or no more work. + ASIO_DECL size_t run(asio::error_code& ec); + + // Run until stopped or one operation is performed. + ASIO_DECL size_t run_one(asio::error_code& ec); + + // Run until timeout, interrupted, or one operation is performed. + ASIO_DECL size_t wait_one(long usec, asio::error_code& ec); + + // Poll for operations without blocking. + ASIO_DECL size_t poll(asio::error_code& ec); + + // Poll for one operation without blocking. + ASIO_DECL size_t poll_one(asio::error_code& ec); + + // Stop the event processing loop. + ASIO_DECL void stop(); + + // Determine whether the io_context is stopped. + bool stopped() const + { + return ::InterlockedExchangeAdd(&stopped_, 0) != 0; + } + + // Restart in preparation for a subsequent run invocation. + void restart() + { + ::InterlockedExchange(&stopped_, 0); + } + + // Notify that some work has started. + void work_started() + { + ::InterlockedIncrement(&outstanding_work_); + } + + // Notify that some work has finished. + void work_finished() + { + if (::InterlockedDecrement(&outstanding_work_) == 0) + stop(); + } + + // Return whether a handler can be dispatched immediately. + bool can_dispatch() + { + return thread_call_stack::contains(this) != 0; + } + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() has not yet been called for the operation. + void post_immediate_completion(win_iocp_operation* op, bool) + { + work_started(); + post_deferred_completion(op); + } + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operation. + ASIO_DECL void post_deferred_completion(win_iocp_operation* op); + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operations. + ASIO_DECL void post_deferred_completions( + op_queue& ops); + + // Request invocation of the given operation using the thread-private queue + // and return immediately. Assumes that work_started() has not yet been + // called for the operation. + void post_private_immediate_completion(win_iocp_operation* op) + { + post_immediate_completion(op, false); + } + + // Request invocation of the given operation using the thread-private queue + // and return immediately. Assumes that work_started() was previously called + // for the operation. + void post_private_deferred_completion(win_iocp_operation* op) + { + post_deferred_completion(op); + } + + // Enqueue the given operation following a failed attempt to dispatch the + // operation for immediate invocation. + void do_dispatch(operation* op) + { + post_immediate_completion(op, false); + } + + // Process unfinished operations as part of a shutdown operation. Assumes + // that work_started() was previously called for the operations. + ASIO_DECL void abandon_operations(op_queue& ops); + + // Called after starting an overlapped I/O operation that did not complete + // immediately. The caller must have already called work_started() prior to + // starting the operation. + ASIO_DECL void on_pending(win_iocp_operation* op); + + // Called after starting an overlapped I/O operation that completed + // immediately. The caller must have already called work_started() prior to + // starting the operation. + ASIO_DECL void on_completion(win_iocp_operation* op, + DWORD last_error = 0, DWORD bytes_transferred = 0); + + // Called after starting an overlapped I/O operation that completed + // immediately. The caller must have already called work_started() prior to + // starting the operation. + ASIO_DECL void on_completion(win_iocp_operation* op, + const asio::error_code& ec, DWORD bytes_transferred = 0); + + // Add a new timer queue to the service. + template + void add_timer_queue(timer_queue& timer_queue); + + // Remove a timer queue from the service. + template + void remove_timer_queue(timer_queue& timer_queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from); + + // Get the concurrency hint that was used to initialise the io_context. + int concurrency_hint() const + { + return concurrency_hint_; + } + +private: +#if defined(WINVER) && (WINVER < 0x0500) + typedef DWORD dword_ptr_t; + typedef ULONG ulong_ptr_t; +#else // defined(WINVER) && (WINVER < 0x0500) + typedef DWORD_PTR dword_ptr_t; + typedef ULONG_PTR ulong_ptr_t; +#endif // defined(WINVER) && (WINVER < 0x0500) + + // Dequeues at most one operation from the I/O completion port, and then + // executes it. Returns the number of operations that were dequeued (i.e. + // either 0 or 1). + ASIO_DECL size_t do_one(DWORD msec, asio::error_code& ec); + + // Helper to calculate the GetQueuedCompletionStatus timeout. + ASIO_DECL static DWORD get_gqcs_timeout(); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Called to recalculate and update the timeout. + ASIO_DECL void update_timeout(); + + // Helper class to call work_finished() on block exit. + struct work_finished_on_block_exit; + + // Helper class for managing a HANDLE. + struct auto_handle + { + HANDLE handle; + auto_handle() : handle(0) {} + ~auto_handle() { if (handle) ::CloseHandle(handle); } + }; + + // The IO completion port used for queueing operations. + auto_handle iocp_; + + // The count of unfinished work. + long outstanding_work_; + + // Flag to indicate whether the event loop has been stopped. + mutable long stopped_; + + // Flag to indicate whether there is an in-flight stop event. Every event + // posted using PostQueuedCompletionStatus consumes non-paged pool, so to + // avoid exhausting this resouce we limit the number of outstanding events. + long stop_event_posted_; + + // Flag to indicate whether the service has been shut down. + long shutdown_; + + enum + { + // Timeout to use with GetQueuedCompletionStatus on older versions of + // Windows. Some versions of windows have a "bug" where a call to + // GetQueuedCompletionStatus can appear stuck even though there are events + // waiting on the queue. Using a timeout helps to work around the issue. + default_gqcs_timeout = 500, + + // Maximum waitable timer timeout, in milliseconds. + max_timeout_msec = 5 * 60 * 1000, + + // Maximum waitable timer timeout, in microseconds. + max_timeout_usec = max_timeout_msec * 1000, + + // Completion key value used to wake up a thread to dispatch timers or + // completed operations. + wake_for_dispatch = 1, + + // Completion key value to indicate that an operation has posted with the + // original last_error and bytes_transferred values stored in the fields of + // the OVERLAPPED structure. + overlapped_contains_result = 2 + }; + + // Timeout to use with GetQueuedCompletionStatus. + const DWORD gqcs_timeout_; + + // Function object for processing timeouts in a background thread. + struct timer_thread_function; + friend struct timer_thread_function; + + // Background thread used for processing timeouts. + scoped_ptr timer_thread_; + + // A waitable timer object used for waiting for timeouts. + auto_handle waitable_timer_; + + // Non-zero if timers or completed operations need to be dispatched. + long dispatch_required_; + + // Mutex for protecting access to the timer queues and completed operations. + mutex dispatch_mutex_; + + // The timer queues. + timer_queue_set timer_queues_; + + // The operations that are ready to dispatch. + op_queue completed_ops_; + + // The concurrency hint used to initialise the io_context. + const int concurrency_hint_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/win_iocp_io_context.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_io_context.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_null_buffers_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_null_buffers_op.hpp new file mode 100644 index 000000000..db70cb28a --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_null_buffers_op.hpp @@ -0,0 +1,121 @@ +// +// detail/win_iocp_null_buffers_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_null_buffers_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_null_buffers_op); + + win_iocp_null_buffers_op(socket_ops::weak_cancel_token_type cancel_token, + Handler& handler) + : reactor_op(&win_iocp_null_buffers_op::do_perform, + &win_iocp_null_buffers_op::do_complete), + cancel_token_(cancel_token), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_null_buffers_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // The reactor may have stored a result in the operation object. + if (o->ec_) + ec = o->ec_; + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_operation.hpp b/subprojects/asio/include/asio/detail/win_iocp_operation.hpp new file mode 100644 index 000000000..81d43f07f --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_operation.hpp @@ -0,0 +1,96 @@ +// +// detail/win_iocp_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OPERATION_HPP +#define ASIO_DETAIL_WIN_IOCP_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/handler_tracking.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_io_context; + +// Base class for all operations. A function pointer is used instead of virtual +// functions to avoid the associated overhead. +class win_iocp_operation + : public OVERLAPPED + ASIO_ALSO_INHERIT_TRACKED_HANDLER +{ +public: + typedef win_iocp_operation operation_type; + + void complete(void* owner, const asio::error_code& ec, + std::size_t bytes_transferred) + { + func_(owner, this, ec, bytes_transferred); + } + + void destroy() + { + func_(0, this, asio::error_code(), 0); + } + +protected: + typedef void (*func_type)( + void*, win_iocp_operation*, + const asio::error_code&, std::size_t); + + win_iocp_operation(func_type func) + : next_(0), + func_(func) + { + reset(); + } + + // Prevents deletion through this type. + ~win_iocp_operation() + { + } + + void reset() + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + hEvent = 0; + ready_ = 0; + } + +private: + friend class op_queue_access; + friend class win_iocp_io_context; + win_iocp_operation* next_; + func_type func_; + long ready_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_overlapped_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_overlapped_op.hpp new file mode 100644 index 000000000..2b2cc319b --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_overlapped_op.hpp @@ -0,0 +1,90 @@ +// +// detail/win_iocp_overlapped_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_overlapped_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_overlapped_op); + + win_iocp_overlapped_op(Handler& handler) + : operation(&win_iocp_overlapped_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_overlapped_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_overlapped_ptr.hpp b/subprojects/asio/include/asio/detail/win_iocp_overlapped_ptr.hpp new file mode 100644 index 000000000..7a1911434 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_overlapped_ptr.hpp @@ -0,0 +1,143 @@ +// +// detail/win_iocp_overlapped_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP +#define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/io_context.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/win_iocp_overlapped_op.hpp" +#include "asio/detail/win_iocp_io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Wraps a handler to create an OVERLAPPED object for use with overlapped I/O. +class win_iocp_overlapped_ptr + : private noncopyable +{ +public: + // Construct an empty win_iocp_overlapped_ptr. + win_iocp_overlapped_ptr() + : ptr_(0), + iocp_service_(0) + { + } + + // Construct an win_iocp_overlapped_ptr to contain the specified handler. + template + explicit win_iocp_overlapped_ptr( + asio::io_context& io_context, ASIO_MOVE_ARG(Handler) handler) + : ptr_(0), + iocp_service_(0) + { + this->reset(io_context, ASIO_MOVE_CAST(Handler)(handler)); + } + + // Destructor automatically frees the OVERLAPPED object unless released. + ~win_iocp_overlapped_ptr() + { + reset(); + } + + // Reset to empty. + void reset() + { + if (ptr_) + { + ptr_->destroy(); + ptr_ = 0; + iocp_service_->work_finished(); + iocp_service_ = 0; + } + } + + // Reset to contain the specified handler, freeing any current OVERLAPPED + // object. + template + void reset(asio::io_context& io_context, Handler handler) + { + typedef win_iocp_overlapped_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((io_context, *p.p, + "io_context", &io_context.impl_, 0, "overlapped")); + + io_context.impl_.work_started(); + reset(); + ptr_ = p.p; + p.v = p.p = 0; + iocp_service_ = &io_context.impl_; + } + + // Get the contained OVERLAPPED object. + OVERLAPPED* get() + { + return ptr_; + } + + // Get the contained OVERLAPPED object. + const OVERLAPPED* get() const + { + return ptr_; + } + + // Release ownership of the OVERLAPPED object. + OVERLAPPED* release() + { + if (ptr_) + iocp_service_->on_pending(ptr_); + + OVERLAPPED* tmp = ptr_; + ptr_ = 0; + iocp_service_ = 0; + return tmp; + } + + // Post completion notification for overlapped operation. Releases ownership. + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + if (ptr_) + { + iocp_service_->on_completion(ptr_, ec, + static_cast(bytes_transferred)); + ptr_ = 0; + iocp_service_ = 0; + } + } + +private: + win_iocp_operation* ptr_; + win_iocp_io_context* iocp_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_serial_port_service.hpp b/subprojects/asio/include/asio/detail/win_iocp_serial_port_service.hpp new file mode 100644 index 000000000..ac06348ec --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_serial_port_service.hpp @@ -0,0 +1,230 @@ +// +// detail/win_iocp_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#include +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Extend win_iocp_handle_service to provide serial port support. +class win_iocp_serial_port_service : + public service_base +{ +public: + // The native type of a serial port. + typedef win_iocp_handle_service::native_handle_type native_handle_type; + + // The implementation type of the serial port. + typedef win_iocp_handle_service::implementation_type implementation_type; + + // Constructor. + ASIO_DECL win_iocp_serial_port_service( + asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new serial port implementation. + void construct(implementation_type& impl) + { + handle_service_.construct(impl); + } + + // Move-construct a new serial port implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + handle_service_.move_construct(impl, other_impl); + } + + // Move-assign from another serial port implementation. + void move_assign(implementation_type& impl, + win_iocp_serial_port_service& other_service, + implementation_type& other_impl) + { + handle_service_.move_assign(impl, + other_service.handle_service_, other_impl); + } + + // Destroy a serial port implementation. + void destroy(implementation_type& impl) + { + handle_service_.destroy(impl); + } + + // Open the serial port using the specified device name. + ASIO_DECL asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec); + + // Assign a native handle to a serial port implementation. + asio::error_code assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) + { + return handle_service_.assign(impl, handle, ec); + } + + // Determine whether the serial port is open. + bool is_open(const implementation_type& impl) const + { + return handle_service_.is_open(impl); + } + + // Destroy a serial port implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return handle_service_.close(impl, ec); + } + + // Get the native serial port representation. + native_handle_type native_handle(implementation_type& impl) + { + return handle_service_.native_handle(impl); + } + + // Cancel all operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return handle_service_.cancel(impl, ec); + } + + // Set an option on the serial port. + template + asio::error_code set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + return do_set_option(impl, + &win_iocp_serial_port_service::store_option, + &option, ec); + } + + // Get an option from the serial port. + template + asio::error_code get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + return do_get_option(impl, + &win_iocp_serial_port_service::load_option, + &option, ec); + } + + // Send a break sequence to the serial port. + asio::error_code send_break(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Write the given data. Returns the number of bytes sent. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return handle_service_.write_some(impl, buffers, ec); + } + + // Start an asynchronous write. The data being written must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, Handler& handler) + { + handle_service_.async_write_some(impl, buffers, handler); + } + + // Read some data. Returns the number of bytes received. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return handle_service_.read_some(impl, buffers, ec); + } + + // Start an asynchronous read. The buffer for the data being received must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler& handler) + { + handle_service_.async_read_some(impl, buffers, handler); + } + +private: + // Function pointer type for storing a serial port option. + typedef asio::error_code (*store_function_type)( + const void*, ::DCB&, asio::error_code&); + + // Helper function template to store a serial port option. + template + static asio::error_code store_option(const void* option, + ::DCB& storage, asio::error_code& ec) + { + static_cast(option)->store(storage, ec); + return ec; + } + + // Helper function to set a serial port option. + ASIO_DECL asio::error_code do_set_option( + implementation_type& impl, store_function_type store, + const void* option, asio::error_code& ec); + + // Function pointer type for loading a serial port option. + typedef asio::error_code (*load_function_type)( + void*, const ::DCB&, asio::error_code&); + + // Helper function template to load a serial port option. + template + static asio::error_code load_option(void* option, + const ::DCB& storage, asio::error_code& ec) + { + static_cast(option)->load(storage, ec); + return ec; + } + + // Helper function to get a serial port option. + ASIO_DECL asio::error_code do_get_option( + const implementation_type& impl, load_function_type load, + void* option, asio::error_code& ec) const; + + // The implementation used for initiating asynchronous operations. + win_iocp_handle_service handle_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_serial_port_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_accept_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_accept_op.hpp new file mode 100644 index 000000000..18e73356c --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_accept_op.hpp @@ -0,0 +1,297 @@ +// +// detail/win_iocp_socket_accept_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/win_iocp_socket_service_base.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_accept_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_accept_op); + + win_iocp_socket_accept_op(win_iocp_socket_service_base& socket_service, + socket_type socket, Socket& peer, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, + bool enable_connection_aborted, Handler& handler) + : operation(&win_iocp_socket_accept_op::do_complete), + socket_service_(socket_service), + socket_(socket), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + socket_holder& new_socket() + { + return new_socket_; + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_accept_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + if (owner) + { + typename Protocol::endpoint peer_endpoint; + std::size_t addr_len = peer_endpoint.capacity(); + socket_ops::complete_iocp_accept(o->socket_, + o->output_buffer(), o->address_length(), + peer_endpoint.data(), &addr_len, + o->new_socket_.get(), ec); + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (ec == asio::error::connection_aborted + && !o->enable_connection_aborted_) + { + o->reset(); + o->socket_service_.restart_accept_op(o->socket_, + o->new_socket_, o->protocol_.family(), + o->protocol_.type(), o->protocol_.protocol(), + o->output_buffer(), o->address_length(), o); + p.v = p.p = 0; + return; + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (!ec) + { + o->peer_.assign(o->protocol_, + typename Socket::native_handle_type( + o->new_socket_.get(), peer_endpoint), ec); + if (!ec) + o->new_socket_.release(); + } + + // Pass endpoint back to caller. + if (o->peer_endpoint_) + *o->peer_endpoint_ = peer_endpoint; + } + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, ec); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + win_iocp_socket_service_base& socket_service_; + socket_type socket_; + socket_holder new_socket_; + Socket& peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; +}; + +#if defined(ASIO_HAS_MOVE) + +template +class win_iocp_socket_move_accept_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_move_accept_op); + + win_iocp_socket_move_accept_op( + win_iocp_socket_service_base& socket_service, socket_type socket, + const Protocol& protocol, asio::io_context& peer_io_context, + typename Protocol::endpoint* peer_endpoint, + bool enable_connection_aborted, Handler& handler) + : operation(&win_iocp_socket_move_accept_op::do_complete), + socket_service_(socket_service), + socket_(socket), + peer_(peer_io_context), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + socket_holder& new_socket() + { + return new_socket_; + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_move_accept_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + if (owner) + { + typename Protocol::endpoint peer_endpoint; + std::size_t addr_len = peer_endpoint.capacity(); + socket_ops::complete_iocp_accept(o->socket_, + o->output_buffer(), o->address_length(), + peer_endpoint.data(), &addr_len, + o->new_socket_.get(), ec); + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (ec == asio::error::connection_aborted + && !o->enable_connection_aborted_) + { + o->reset(); + o->socket_service_.restart_accept_op(o->socket_, + o->new_socket_, o->protocol_.family(), + o->protocol_.type(), o->protocol_.protocol(), + o->output_buffer(), o->address_length(), o); + p.v = p.p = 0; + return; + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (!ec) + { + o->peer_.assign(o->protocol_, + typename Protocol::socket::native_handle_type( + o->new_socket_.get(), peer_endpoint), ec); + if (!ec) + o->new_socket_.release(); + } + + // Pass endpoint back to caller. + if (o->peer_endpoint_) + *o->peer_endpoint_ = peer_endpoint; + } + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::move_binder2 + handler(0, ASIO_MOVE_CAST(Handler)(o->handler_), ec, + ASIO_MOVE_CAST(typename Protocol::socket)(o->peer_)); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + win_iocp_socket_service_base& socket_service_; + socket_type socket_; + socket_holder new_socket_; + typename Protocol::socket peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; +}; + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_connect_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_connect_op.hpp new file mode 100644 index 000000000..e0c52dc97 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_connect_op.hpp @@ -0,0 +1,127 @@ +// +// detail/win_iocp_socket_connect_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_socket_connect_op_base : public reactor_op +{ +public: + win_iocp_socket_connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(&win_iocp_socket_connect_op_base::do_perform, complete_func), + socket_(socket), + connect_ex_(false) + { + } + + static status do_perform(reactor_op* base) + { + win_iocp_socket_connect_op_base* o( + static_cast(base)); + + return socket_ops::non_blocking_connect( + o->socket_, o->ec_) ? done : not_done; + } + + socket_type socket_; + bool connect_ex_; +}; + +template +class win_iocp_socket_connect_op : public win_iocp_socket_connect_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_connect_op); + + win_iocp_socket_connect_op(socket_type socket, Handler& handler) + : win_iocp_socket_connect_op_base(socket, + &win_iocp_socket_connect_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_connect_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + if (owner) + { + if (o->connect_ex_) + socket_ops::complete_iocp_connect(o->socket_, ec); + else + ec = o->ec_; + } + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, ec); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_recv_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_recv_op.hpp new file mode 100644 index 000000000..41595405d --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_recv_op.hpp @@ -0,0 +1,117 @@ +// +// detail/win_iocp_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recv_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recv_op); + + win_iocp_socket_recv_op(socket_ops::state_type state, + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler& handler) + : operation(&win_iocp_socket_recv_op::do_complete), + state_(state), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_recv_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recv(o->state_, o->cancel_token_, + buffer_sequence_adapter::all_empty(o->buffers_), + ec, bytes_transferred); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::state_type state_; + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_recvfrom_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_recvfrom_op.hpp new file mode 100644 index 000000000..843ce5bba --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_recvfrom_op.hpp @@ -0,0 +1,125 @@ +// +// detail/win_iocp_socket_recvfrom_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recvfrom_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recvfrom_op); + + win_iocp_socket_recvfrom_op(Endpoint& endpoint, + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler& handler) + : operation(&win_iocp_socket_recvfrom_op::do_complete), + endpoint_(endpoint), + endpoint_size_(static_cast(endpoint.capacity())), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + int& endpoint_size() + { + return endpoint_size_; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_recvfrom_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recvfrom(o->cancel_token_, ec); + + // Record the size of the endpoint returned by the operation. + o->endpoint_.resize(o->endpoint_size_); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Endpoint& endpoint_; + int endpoint_size_; + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_recvmsg_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_recvmsg_op.hpp new file mode 100644 index 000000000..78f36b7f3 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_recvmsg_op.hpp @@ -0,0 +1,118 @@ +// +// detail/win_iocp_socket_recvmsg_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECVMSG_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECVMSG_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" +#include "asio/socket_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recvmsg_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recvmsg_op); + + win_iocp_socket_recvmsg_op( + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, + socket_base::message_flags& out_flags, Handler& handler) + : operation(&win_iocp_socket_recvmsg_op::do_complete), + cancel_token_(cancel_token), + buffers_(buffers), + out_flags_(out_flags), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_recvmsg_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recvmsg(o->cancel_token_, ec); + o->out_flags_ = 0; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + socket_base::message_flags& out_flags_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECVMSG_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_send_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_send_op.hpp new file mode 100644 index 000000000..e1c7ab93c --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_send_op.hpp @@ -0,0 +1,111 @@ +// +// detail/win_iocp_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_send_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_send_op); + + win_iocp_socket_send_op(socket_ops::weak_cancel_token_type cancel_token, + const ConstBufferSequence& buffers, Handler& handler) + : operation(&win_iocp_socket_send_op::do_complete), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_send_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_send(o->cancel_token_, ec); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + ConstBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_service.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_service.hpp new file mode 100644 index 000000000..3f01b4d3e --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_service.hpp @@ -0,0 +1,599 @@ +// +// detail/win_iocp_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_context.hpp" +#include "asio/detail/win_iocp_null_buffers_op.hpp" +#include "asio/detail/win_iocp_socket_accept_op.hpp" +#include "asio/detail/win_iocp_socket_connect_op.hpp" +#include "asio/detail/win_iocp_socket_recvfrom_op.hpp" +#include "asio/detail/win_iocp_socket_send_op.hpp" +#include "asio/detail/win_iocp_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_service : + public service_base >, + public win_iocp_socket_service_base +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + class native_handle_type + { + public: + native_handle_type(socket_type s) + : socket_(s), + have_remote_endpoint_(false) + { + } + + native_handle_type(socket_type s, const endpoint_type& ep) + : socket_(s), + have_remote_endpoint_(true), + remote_endpoint_(ep) + { + } + + void operator=(socket_type s) + { + socket_ = s; + have_remote_endpoint_ = false; + remote_endpoint_ = endpoint_type(); + } + + operator socket_type() const + { + return socket_; + } + + bool have_remote_endpoint() const + { + return have_remote_endpoint_; + } + + endpoint_type remote_endpoint() const + { + return remote_endpoint_; + } + + private: + socket_type socket_; + bool have_remote_endpoint_; + endpoint_type remote_endpoint_; + }; + + // The implementation type of the socket. + struct implementation_type : + win_iocp_socket_service_base::base_implementation_type + { + // Default constructor. + implementation_type() + : protocol_(endpoint_type().protocol()), + have_remote_endpoint_(false), + remote_endpoint_() + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + + // Whether we have a cached remote endpoint. + bool have_remote_endpoint_; + + // A cached remote endpoint. + endpoint_type remote_endpoint_; + }; + + // Constructor. + win_iocp_socket_service(asio::io_context& io_context) + : service_base >(io_context), + win_iocp_socket_service_base(io_context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + + impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_; + other_impl.have_remote_endpoint_ = false; + + impl.remote_endpoint_ = other_impl.remote_endpoint_; + other_impl.remote_endpoint_ = endpoint_type(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + win_iocp_socket_service_base& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + + impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_; + other_impl.have_remote_endpoint_ = false; + + impl.remote_endpoint_ = other_impl.remote_endpoint_; + other_impl.remote_endpoint_ = endpoint_type(); + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type& impl, + win_iocp_socket_service&, + typename win_iocp_socket_service< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + + impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_; + other_impl.have_remote_endpoint_ = false; + + impl.remote_endpoint_ = other_impl.remote_endpoint_; + other_impl.remote_endpoint_ = typename Protocol1::endpoint(); + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) + { + impl.protocol_ = protocol; + impl.have_remote_endpoint_ = false; + impl.remote_endpoint_ = endpoint_type(); + } + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + if (!do_assign(impl, protocol.type(), native_socket, ec)) + { + impl.protocol_ = protocol; + impl.have_remote_endpoint_ = native_socket.have_remote_endpoint(); + impl.remote_endpoint_ = native_socket.remote_endpoint(); + } + return ec; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type& impl) + { + if (impl.have_remote_endpoint_) + return native_handle_type(impl.socket_, impl.remote_endpoint_); + return native_handle_type(impl.socket_); + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint = impl.remote_endpoint_; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), + &addr_len, impl.have_remote_endpoint_, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_send_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, buffers, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_send_to")); + + buffer_sequence_adapter bufs(buffers); + + start_send_to_op(impl, bufs.buffers(), bufs.count(), + destination.data(), static_cast(destination.size()), + flags, p.p); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_send_to(null_buffers)")); + + start_reactor_op(impl, select_reactor::write_op, p.p); + p.v = p.p = 0; + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); + + if (!ec) + sender_endpoint.resize(addr_len); + + return bytes_recvd; + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endp, + socket_base::message_flags flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_recvfrom_op< + MutableBufferSequence, endpoint_type, Handler> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(sender_endp, impl.cancel_token_, buffers, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_from")); + + buffer_sequence_adapter bufs(buffers); + + start_receive_from_op(impl, bufs.buffers(), bufs.count(), + sender_endp.data(), flags, &p.p->endpoint_size(), p.p); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_from(null_buffers)")); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + start_null_buffers_receive_op(impl, flags, p.p); + p.v = p.p = 0; + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, asio::error_code& ec) + { + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return ec; + } + +#if defined(ASIO_HAS_MOVE) + // Accept a new connection. + typename Protocol::socket accept(implementation_type& impl, + io_context* peer_io_context, endpoint_type* peer_endpoint, + asio::error_code& ec) + { + typename Protocol::socket peer( + peer_io_context ? *peer_io_context : io_context_); + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return peer; + } +#endif // defined(ASIO_HAS_MOVE) + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_accept_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + bool enable_connection_aborted = + (impl.state_ & socket_ops::enable_connection_aborted) != 0; + p.p = new (p.v) op(*this, impl.socket_, peer, impl.protocol_, + peer_endpoint, enable_connection_aborted, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, peer.is_open(), p.p->new_socket(), + impl.protocol_.family(), impl.protocol_.type(), + impl.protocol_.protocol(), p.p->output_buffer(), + p.p->address_length(), p.p); + p.v = p.p = 0; + } + +#if defined(ASIO_HAS_MOVE) + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept(implementation_type& impl, + asio::io_context* peer_io_context, + endpoint_type* peer_endpoint, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_move_accept_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + bool enable_connection_aborted = + (impl.state_ & socket_ops::enable_connection_aborted) != 0; + p.p = new (p.v) op(*this, impl.socket_, impl.protocol_, + peer_io_context ? *peer_io_context : io_context_, + peer_endpoint, enable_connection_aborted, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, false, p.p->new_socket(), + impl.protocol_.family(), impl.protocol_.type(), + impl.protocol_.protocol(), p.p->output_buffer(), + p.p->address_length(), p.p); + p.v = p.p = 0; + } +#endif // defined(ASIO_HAS_MOVE) + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + socket_ops::sync_connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_connect_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_connect")); + + start_connect_op(impl, impl.protocol_.family(), impl.protocol_.type(), + peer_endpoint.data(), static_cast(peer_endpoint.size()), p.p); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_socket_service_base.hpp b/subprojects/asio/include/asio/detail/win_iocp_socket_service_base.hpp new file mode 100644 index 000000000..ef1d7180e --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_socket_service_base.hpp @@ -0,0 +1,591 @@ +// +// detail/win_iocp_socket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_context.hpp" +#include "asio/detail/win_iocp_null_buffers_op.hpp" +#include "asio/detail/win_iocp_socket_connect_op.hpp" +#include "asio/detail/win_iocp_socket_send_op.hpp" +#include "asio/detail/win_iocp_socket_recv_op.hpp" +#include "asio/detail/win_iocp_socket_recvmsg_op.hpp" +#include "asio/detail/win_iocp_wait_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_socket_service_base +{ +public: + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // We use a shared pointer as a cancellation token here to work around the + // broken Windows support for cancellation. MSDN says that when you call + // closesocket any outstanding WSARecv or WSASend operations will complete + // with the error ERROR_OPERATION_ABORTED. In practice they complete with + // ERROR_NETNAME_DELETED, which means you can't tell the difference between + // a local cancellation and the socket being hard-closed by the peer. + socket_ops::shared_cancel_token_type cancel_token_; + + // Per-descriptor data used by the reactor. + select_reactor::per_descriptor_data reactor_data_; + +#if defined(ASIO_ENABLE_CANCELIO) + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the socket. + DWORD safe_cancellation_thread_id_; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Pointers to adjacent socket implementations in linked list. + base_implementation_type* next_; + base_implementation_type* prev_; + }; + + // Constructor. + ASIO_DECL win_iocp_socket_service_base( + asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type& impl); + + // Move-construct a new socket implementation. + ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl); + + // Move-assign from another socket implementation. + ASIO_DECL void base_move_assign(base_implementation_type& impl, + win_iocp_socket_service_base& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Release ownership of the socket. + ASIO_DECL socket_type release( + base_implementation_type& impl, asio::error_code& ec); + + // Cancel all operations associated with the socket. + ASIO_DECL asio::error_code cancel( + base_implementation_type& impl, asio::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(base_implementation_type& impl, + int backlog, asio::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_user_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Wait for the socket to become ready to read, ready to write, or to have + // pending error conditions. + asio::error_code wait(base_implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + switch (w) + { + case socket_base::wait_read: + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_write: + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_error: + socket_ops::poll_error(impl.socket_, impl.state_, -1, ec); + break; + default: + ec = asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the socket to become ready to read, ready to + // write, or to have pending error conditions. + template + void async_wait(base_implementation_type& impl, + socket_base::wait_type w, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_wait_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_wait")); + + switch (w) + { + case socket_base::wait_read: + start_null_buffers_receive_op(impl, 0, p.p); + break; + case socket_base::wait_write: + start_reactor_op(impl, select_reactor::write_op, p.p); + break; + case socket_base::wait_error: + start_reactor_op(impl, select_reactor::except_op, p.p); + break; + default: + p.p->ec_ = asio::error::invalid_argument; + iocp_service_.post_immediate_completion(p.p, is_continuation); + break; + } + + p.v = p.p = 0; + } + + // Send the given data to the peer. Returns the number of bytes sent. + template + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_send_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, buffers, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_send")); + + buffer_sequence_adapter bufs(buffers); + + start_send_op(impl, bufs.buffers(), bufs.count(), flags, + (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), + p.p); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_send(null_buffers)")); + + start_reactor_op(impl, select_reactor::write_op, p.p); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_recv_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.state_, impl.cancel_token_, buffers, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_receive")); + + buffer_sequence_adapter bufs(buffers); + + start_receive_op(impl, bufs.buffers(), bufs.count(), flags, + (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), + p.p); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_receive(null_buffers)")); + + start_null_buffers_receive_op(impl, flags, p.p); + p.v = p.p = 0; + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template + size_t receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recvmsg(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), in_flags, out_flags, ec); + } + + // Wait until data can be received without blocking. + size_t receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_recvmsg_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, buffers, out_flags, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags")); + + buffer_sequence_adapter bufs(buffers); + + start_receive_op(impl, bufs.buffers(), bufs.count(), in_flags, false, p.p); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + ASIO_HANDLER_CREATION((io_context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags(null_buffers)")); + + // Reset out_flags since it can be given no sensible value at this time. + out_flags = 0; + + start_null_buffers_receive_op(impl, in_flags, p.p); + p.v = p.p = 0; + } + + // Helper function to restart an asynchronous accept operation. + ASIO_DECL void restart_accept_op(socket_type s, + socket_holder& new_socket, int family, int type, int protocol, + void* output_buffer, DWORD address_length, operation* op); + +protected: + // Open a new socket implementation. + ASIO_DECL asio::error_code do_open( + base_implementation_type& impl, int family, int type, + int protocol, asio::error_code& ec); + + // Assign a native socket to a socket implementation. + ASIO_DECL asio::error_code do_assign( + base_implementation_type& impl, int type, + socket_type native_socket, asio::error_code& ec); + + // Helper function to start an asynchronous send operation. + ASIO_DECL void start_send_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op); + + // Helper function to start an asynchronous send_to operation. + ASIO_DECL void start_send_to_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + const socket_addr_type* addr, int addrlen, + socket_base::message_flags flags, operation* op); + + // Helper function to start an asynchronous receive operation. + ASIO_DECL void start_receive_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op); + + // Helper function to start an asynchronous null_buffers receive operation. + ASIO_DECL void start_null_buffers_receive_op( + base_implementation_type& impl, + socket_base::message_flags flags, reactor_op* op); + + // Helper function to start an asynchronous receive_from operation. + ASIO_DECL void start_receive_from_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, + socket_base::message_flags flags, int* addrlen, operation* op); + + // Helper function to start an asynchronous accept operation. + ASIO_DECL void start_accept_op(base_implementation_type& impl, + bool peer_is_open, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op); + + // Start an asynchronous read or write operation using the reactor. + ASIO_DECL void start_reactor_op(base_implementation_type& impl, + int op_type, reactor_op* op); + + // Start the asynchronous connect operation using the reactor. + ASIO_DECL void start_connect_op(base_implementation_type& impl, + int family, int type, const socket_addr_type* remote_addr, + std::size_t remote_addrlen, win_iocp_socket_connect_op_base* op); + + // Helper function to close a socket when the associated object is being + // destroyed. + ASIO_DECL void close_for_destruction(base_implementation_type& impl); + + // Update the ID of the thread from which cancellation is safe. + ASIO_DECL void update_cancellation_thread_id( + base_implementation_type& impl); + + // Helper function to get the reactor. If no reactor has been created yet, a + // new one is obtained from the io_context and a pointer to it is cached in + // this service. + ASIO_DECL select_reactor& get_reactor(); + + // The type of a ConnectEx function pointer, as old SDKs may not provide it. + typedef BOOL (PASCAL *connect_ex_fn)(SOCKET, + const socket_addr_type*, int, void*, DWORD, DWORD*, OVERLAPPED*); + + // Helper function to get the ConnectEx pointer. If no ConnectEx pointer has + // been obtained yet, one is obtained using WSAIoctl and the pointer is + // cached. Returns a null pointer if ConnectEx is not available. + ASIO_DECL connect_ex_fn get_connect_ex( + base_implementation_type& impl, int type); + + // The type of a NtSetInformationFile function pointer. + typedef LONG (NTAPI *nt_set_info_fn)(HANDLE, ULONG_PTR*, void*, ULONG, ULONG); + + // Helper function to get the NtSetInformationFile function pointer. If no + // NtSetInformationFile pointer has been obtained yet, one is obtained using + // GetProcAddress and the pointer is cached. Returns a null pointer if + // NtSetInformationFile is not available. + ASIO_DECL nt_set_info_fn get_nt_set_info(); + + // Helper function to emulate InterlockedCompareExchangePointer functionality + // for: + // - very old Platform SDKs; and + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + ASIO_DECL void* interlocked_compare_exchange_pointer( + void** dest, void* exch, void* cmp); + + // Helper function to emulate InterlockedExchangePointer functionality for: + // - very old Platform SDKs; and + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + ASIO_DECL void* interlocked_exchange_pointer(void** dest, void* val); + + // The io_context used to obtain the reactor, if required. + asio::io_context& io_context_; + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_context& iocp_service_; + + // The reactor used for performing connect operations. This object is created + // only if needed. + select_reactor* reactor_; + + // Pointer to ConnectEx implementation. + void* connect_ex_; + + // Pointer to NtSetInformationFile implementation. + void* nt_set_info_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + base_implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_socket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_thread_info.hpp b/subprojects/asio/include/asio/detail/win_iocp_thread_info.hpp new file mode 100644 index 000000000..13181e2ec --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_thread_info.hpp @@ -0,0 +1,34 @@ +// +// detail/win_iocp_thread_info.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_THREAD_INFO_HPP +#define ASIO_DETAIL_WIN_IOCP_THREAD_INFO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/thread_info_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct win_iocp_thread_info : public thread_info_base +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_THREAD_INFO_HPP diff --git a/subprojects/asio/include/asio/detail/win_iocp_wait_op.hpp b/subprojects/asio/include/asio/detail/win_iocp_wait_op.hpp new file mode 100644 index 000000000..472eea38a --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_iocp_wait_op.hpp @@ -0,0 +1,121 @@ +// +// detail/win_iocp_wait_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_WAIT_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_WAIT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_wait_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_wait_op); + + win_iocp_wait_op(socket_ops::weak_cancel_token_type cancel_token, + Handler& handler) + : reactor_op(&win_iocp_wait_op::do_perform, + &win_iocp_wait_op::do_complete), + cancel_token_(cancel_token), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_wait_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // The reactor may have stored a result in the operation object. + if (o->ec_) + ec = o->ec_; + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, ec); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_WAIT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/win_mutex.hpp b/subprojects/asio/include/asio/detail/win_mutex.hpp new file mode 100644 index 000000000..ce52a2f79 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_mutex.hpp @@ -0,0 +1,78 @@ +// +// detail/win_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_MUTEX_HPP +#define ASIO_DETAIL_WIN_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + ASIO_DECL win_mutex(); + + // Destructor. + ~win_mutex() + { + ::DeleteCriticalSection(&crit_section_); + } + + // Lock the mutex. + void lock() + { + ::EnterCriticalSection(&crit_section_); + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + +private: + // Initialisation must be performed in a separate function to the constructor + // since the compiler does not support the use of structured exceptions and + // C++ exceptions in the same function. + ASIO_DECL int do_init(); + + ::CRITICAL_SECTION crit_section_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/win_object_handle_service.hpp b/subprojects/asio/include/asio/detail/win_object_handle_service.hpp new file mode 100644 index 000000000..1d8abc9c7 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_object_handle_service.hpp @@ -0,0 +1,184 @@ +// +// detail/win_object_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2011 Boris Schaeling (boris@highscore.de) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_OBJECT_HANDLE_SERVICE_HPP +#define ASIO_DETAIL_WIN_OBJECT_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/wait_handler.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_object_handle_service : + public service_base +{ +public: + // The native type of an object handle. + typedef HANDLE native_handle_type; + + // The implementation type of the object handle. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : handle_(INVALID_HANDLE_VALUE), + wait_handle_(INVALID_HANDLE_VALUE), + owner_(0), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_object_handle_service; + + // The native object handle representation. May be accessed or modified + // without locking the mutex. + native_handle_type handle_; + + // The handle used to unregister the wait operation. The mutex must be + // locked when accessing or modifying this member. + HANDLE wait_handle_; + + // The operations waiting on the object handle. If there is a registered + // wait then the mutex must be locked when accessing or modifying this + // member + op_queue op_queue_; + + // The service instance that owns the object handle implementation. + win_object_handle_service* owner_; + + // Pointers to adjacent handle implementations in linked list. The mutex + // must be locked when accessing or modifying these members. + implementation_type* next_; + implementation_type* prev_; + }; + + // Constructor. + ASIO_DECL win_object_handle_service( + asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new handle implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Move-construct a new handle implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl); + + // Move-assign from another handle implementation. + ASIO_DECL void move_assign(implementation_type& impl, + win_object_handle_service& other_service, + implementation_type& other_impl); + + // Destroy a handle implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Assign a native handle to a handle implementation. + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec); + + // Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return impl.handle_ != INVALID_HANDLE_VALUE && impl.handle_ != 0; + } + + // Destroy a handle implementation. + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); + + // Get the native handle representation. + native_handle_type native_handle(const implementation_type& impl) const + { + return impl.handle_; + } + + // Cancel all operations associated with the handle. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Perform a synchronous wait for the object to enter a signalled state. + ASIO_DECL void wait(implementation_type& impl, + asio::error_code& ec); + + /// Start an asynchronous wait. + template + void async_wait(implementation_type& impl, Handler& handler) + { + // Allocate and construct an operation to wrap the handler. + typedef wait_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((io_context_.context(), *p.p, "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "async_wait")); + + start_wait_op(impl, p.p); + p.v = p.p = 0; + } + +private: + // Helper function to start an asynchronous wait operation. + ASIO_DECL void start_wait_op(implementation_type& impl, wait_op* op); + + // Helper function to register a wait operation. + ASIO_DECL void register_wait_callback( + implementation_type& impl, mutex::scoped_lock& lock); + + // Callback function invoked when the registered wait completes. + static ASIO_DECL VOID CALLBACK wait_callback( + PVOID param, BOOLEAN timeout); + + // The io_context implementation used to post completions. + io_context_impl& io_context_; + + // Mutex to protect access to internal state. + mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; + + // Flag to indicate that the dispatcher has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_object_handle_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#endif // ASIO_DETAIL_WIN_OBJECT_HANDLE_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/win_static_mutex.hpp b/subprojects/asio/include/asio/detail/win_static_mutex.hpp new file mode 100644 index 000000000..b16968862 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_static_mutex.hpp @@ -0,0 +1,74 @@ +// +// detail/win_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_STATIC_MUTEX_HPP +#define ASIO_DETAIL_WIN_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct win_static_mutex +{ + typedef asio::detail::scoped_lock scoped_lock; + + // Initialise the mutex. + ASIO_DECL void init(); + + // Initialisation must be performed in a separate function to the "public" + // init() function since the compiler does not support the use of structured + // exceptions and C++ exceptions in the same function. + ASIO_DECL int do_init(); + + // Lock the mutex. + void lock() + { + ::EnterCriticalSection(&crit_section_); + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + + bool initialised_; + ::CRITICAL_SECTION crit_section_; +}; + +#if defined(UNDER_CE) +# define ASIO_WIN_STATIC_MUTEX_INIT { false, { 0, 0, 0, 0, 0 } } +#else // defined(UNDER_CE) +# define ASIO_WIN_STATIC_MUTEX_INIT { false, { 0, 0, 0, 0, 0, 0 } } +#endif // defined(UNDER_CE) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_static_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_STATIC_MUTEX_HPP diff --git a/subprojects/asio/include/asio/detail/win_thread.hpp b/subprojects/asio/include/asio/detail/win_thread.hpp new file mode 100644 index 000000000..8b28a90b6 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_thread.hpp @@ -0,0 +1,147 @@ +// +// detail/win_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_THREAD_HPP +#define ASIO_DETAIL_WIN_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_APP) \ + && !defined(UNDER_CE) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); + +#if defined(WINVER) && (WINVER < 0x0500) +ASIO_DECL void __stdcall apc_function(ULONG data); +#else +ASIO_DECL void __stdcall apc_function(ULONG_PTR data); +#endif + +template +class win_thread_base +{ +public: + static bool terminate_threads() + { + return ::InterlockedExchangeAdd(&terminate_threads_, 0) != 0; + } + + static void set_terminate_threads(bool b) + { + ::InterlockedExchange(&terminate_threads_, b ? 1 : 0); + } + +private: + static long terminate_threads_; +}; + +template +long win_thread_base::terminate_threads_ = 0; + +class win_thread + : private noncopyable, + public win_thread_base +{ +public: + // Constructor. + template + win_thread(Function f, unsigned int stack_size = 0) + : thread_(0), + exit_event_(0) + { + start_thread(new func(f), stack_size); + } + + // Destructor. + ASIO_DECL ~win_thread(); + + // Wait for the thread to exit. + ASIO_DECL void join(); + + // Get number of CPUs. + ASIO_DECL static std::size_t hardware_concurrency(); + +private: + friend ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); + +#if defined(WINVER) && (WINVER < 0x0500) + friend ASIO_DECL void __stdcall apc_function(ULONG); +#else + friend ASIO_DECL void __stdcall apc_function(ULONG_PTR); +#endif + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + ::HANDLE entry_event_; + ::HANDLE exit_event_; + }; + + struct auto_func_base_ptr + { + func_base* ptr; + ~auto_func_base_ptr() { delete ptr; } + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ASIO_DECL void start_thread(func_base* arg, unsigned int stack_size); + + ::HANDLE thread_; + ::HANDLE exit_event_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_thread.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_APP) + // && !defined(UNDER_CE) + +#endif // ASIO_DETAIL_WIN_THREAD_HPP diff --git a/subprojects/asio/include/asio/detail/win_tss_ptr.hpp b/subprojects/asio/include/asio/detail/win_tss_ptr.hpp new file mode 100644 index 000000000..4207108a3 --- /dev/null +++ b/subprojects/asio/include/asio/detail/win_tss_ptr.hpp @@ -0,0 +1,79 @@ +// +// detail/win_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_TSS_PTR_HPP +#define ASIO_DETAIL_WIN_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper function to create thread-specific storage. +ASIO_DECL DWORD win_tss_ptr_create(); + +template +class win_tss_ptr + : private noncopyable +{ +public: + // Constructor. + win_tss_ptr() + : tss_key_(win_tss_ptr_create()) + { + } + + // Destructor. + ~win_tss_ptr() + { + ::TlsFree(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::TlsGetValue(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::TlsSetValue(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + DWORD tss_key_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_tss_ptr.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_TSS_PTR_HPP diff --git a/subprojects/asio/include/asio/detail/winapp_thread.hpp b/subprojects/asio/include/asio/detail/winapp_thread.hpp new file mode 100644 index 000000000..69dcf08d4 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winapp_thread.hpp @@ -0,0 +1,124 @@ +// +// detail/winapp_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINAPP_THREAD_HPP +#define ASIO_DETAIL_WINAPP_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) && defined(ASIO_WINDOWS_APP) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +DWORD WINAPI winapp_thread_function(LPVOID arg); + +class winapp_thread + : private noncopyable +{ +public: + // Constructor. + template + winapp_thread(Function f, unsigned int = 0) + { + scoped_ptr arg(new func(f)); + DWORD thread_id = 0; + thread_ = ::CreateThread(0, 0, winapp_thread_function, + arg.get(), 0, &thread_id); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } + arg.release(); + } + + // Destructor. + ~winapp_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObjectEx(thread_, INFINITE, false); + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + SYSTEM_INFO system_info; + ::GetNativeSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; + } + +private: + friend DWORD WINAPI winapp_thread_function(LPVOID arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline DWORD WINAPI winapp_thread_function(LPVOID arg) +{ + scoped_ptr func( + static_cast(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) && defined(ASIO_WINDOWS_APP) + +#endif // ASIO_DETAIL_WINAPP_THREAD_HPP diff --git a/subprojects/asio/include/asio/detail/wince_thread.hpp b/subprojects/asio/include/asio/detail/wince_thread.hpp new file mode 100644 index 000000000..a2863f625 --- /dev/null +++ b/subprojects/asio/include/asio/detail/wince_thread.hpp @@ -0,0 +1,124 @@ +// +// detail/wince_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINCE_THREAD_HPP +#define ASIO_DETAIL_WINCE_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) && defined(UNDER_CE) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +DWORD WINAPI wince_thread_function(LPVOID arg); + +class wince_thread + : private noncopyable +{ +public: + // Constructor. + template + wince_thread(Function f, unsigned int = 0) + { + scoped_ptr arg(new func(f)); + DWORD thread_id = 0; + thread_ = ::CreateThread(0, 0, wince_thread_function, + arg.get(), 0, &thread_id); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } + arg.release(); + } + + // Destructor. + ~wince_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObject(thread_, INFINITE); + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + SYSTEM_INFO system_info; + ::GetSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; + } + +private: + friend DWORD WINAPI wince_thread_function(LPVOID arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline DWORD WINAPI wince_thread_function(LPVOID arg) +{ + scoped_ptr func( + static_cast(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) && defined(UNDER_CE) + +#endif // ASIO_DETAIL_WINCE_THREAD_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_async_manager.hpp b/subprojects/asio/include/asio/detail/winrt_async_manager.hpp new file mode 100644 index 000000000..e22ad52e2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_async_manager.hpp @@ -0,0 +1,294 @@ +// +// detail/winrt_async_manager.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP +#define ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_async_manager + : public asio::detail::service_base +{ +public: + // Constructor. + winrt_async_manager(asio::io_context& io_context) + : asio::detail::service_base(io_context), + io_context_(use_service(io_context)), + outstanding_ops_(1) + { + } + + // Destructor. + ~winrt_async_manager() + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + if (--outstanding_ops_ > 0) + { + // Block until last operation is complete. + std::future f = promise_.get_future(); + f.wait(); + } + } + + void sync(Windows::Foundation::IAsyncAction^ action, + asio::error_code& ec) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + action->Completed = ref new AsyncActionCompletedHandler( + [promise](IAsyncAction^ action, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + promise->set_value(asio::error::operation_aborted); + break; + case AsyncStatus::Error: + case AsyncStatus::Completed: + default: + asio::error_code ec( + action->ErrorCode.Value, + asio::system_category()); + promise->set_value(ec); + break; + } + }); + + ec = future.get(); + } + + template + TResult sync(Windows::Foundation::IAsyncOperation^ operation, + asio::error_code& ec) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + operation->Completed = ref new AsyncOperationCompletedHandler( + [promise](IAsyncOperation^ operation, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + promise->set_value(asio::error::operation_aborted); + break; + case AsyncStatus::Error: + case AsyncStatus::Completed: + default: + asio::error_code ec( + operation->ErrorCode.Value, + asio::system_category()); + promise->set_value(ec); + break; + } + }); + + ec = future.get(); + return operation->GetResults(); + } + + template + TResult sync( + Windows::Foundation::IAsyncOperationWithProgress< + TResult, TProgress>^ operation, + asio::error_code& ec) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + operation->Completed + = ref new AsyncOperationWithProgressCompletedHandler( + [promise](IAsyncOperationWithProgress^ operation, + AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + promise->set_value(asio::error::operation_aborted); + break; + case AsyncStatus::Started: + break; + case AsyncStatus::Error: + case AsyncStatus::Completed: + default: + asio::error_code ec( + operation->ErrorCode.Value, + asio::system_category()); + promise->set_value(ec); + break; + } + }); + + ec = future.get(); + return operation->GetResults(); + } + + void async(Windows::Foundation::IAsyncAction^ action, + winrt_async_op* handler) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto on_completed = ref new AsyncActionCompletedHandler( + [this, handler](IAsyncAction^ action, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + handler->ec_ = asio::error::operation_aborted; + break; + case AsyncStatus::Started: + return; + case AsyncStatus::Completed: + case AsyncStatus::Error: + default: + handler->ec_ = asio::error_code( + action->ErrorCode.Value, + asio::system_category()); + break; + } + io_context_.post_deferred_completion(handler); + if (--outstanding_ops_ == 0) + promise_.set_value(); + }); + + io_context_.work_started(); + ++outstanding_ops_; + action->Completed = on_completed; + } + + template + void async(Windows::Foundation::IAsyncOperation^ operation, + winrt_async_op* handler) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto on_completed = ref new AsyncOperationCompletedHandler( + [this, handler](IAsyncOperation^ operation, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + handler->ec_ = asio::error::operation_aborted; + break; + case AsyncStatus::Started: + return; + case AsyncStatus::Completed: + handler->result_ = operation->GetResults(); + // Fall through. + case AsyncStatus::Error: + default: + handler->ec_ = asio::error_code( + operation->ErrorCode.Value, + asio::system_category()); + break; + } + io_context_.post_deferred_completion(handler); + if (--outstanding_ops_ == 0) + promise_.set_value(); + }); + + io_context_.work_started(); + ++outstanding_ops_; + operation->Completed = on_completed; + } + + template + void async( + Windows::Foundation::IAsyncOperationWithProgress< + TResult, TProgress>^ operation, + winrt_async_op* handler) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto on_completed + = ref new AsyncOperationWithProgressCompletedHandler( + [this, handler](IAsyncOperationWithProgress< + TResult, TProgress>^ operation, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + handler->ec_ = asio::error::operation_aborted; + break; + case AsyncStatus::Started: + return; + case AsyncStatus::Completed: + handler->result_ = operation->GetResults(); + // Fall through. + case AsyncStatus::Error: + default: + handler->ec_ = asio::error_code( + operation->ErrorCode.Value, + asio::system_category()); + break; + } + io_context_.post_deferred_completion(handler); + if (--outstanding_ops_ == 0) + promise_.set_value(); + }); + + io_context_.work_started(); + ++outstanding_ops_; + operation->Completed = on_completed; + } + +private: + // The io_context implementation used to post completed handlers. + io_context_impl& io_context_; + + // Count of outstanding operations. + atomic_count outstanding_ops_; + + // Used to keep wait for outstanding operations to complete. + std::promise promise_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_async_op.hpp b/subprojects/asio/include/asio/detail/winrt_async_op.hpp new file mode 100644 index 000000000..75891a4ad --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_async_op.hpp @@ -0,0 +1,65 @@ +// +// detail/winrt_async_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_ASYNC_OP_HPP +#define ASIO_DETAIL_WINRT_ASYNC_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_async_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + + // The result of the operation, to be passed to the completion handler. + TResult result_; + +protected: + winrt_async_op(func_type complete_func) + : operation(complete_func), + result_() + { + } +}; + +template <> +class winrt_async_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + +protected: + winrt_async_op(func_type complete_func) + : operation(complete_func) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WINRT_ASYNC_OP_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_resolve_op.hpp b/subprojects/asio/include/asio/detail/winrt_resolve_op.hpp new file mode 100644 index 000000000..07efd29d2 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_resolve_op.hpp @@ -0,0 +1,118 @@ +// +// detail/winrt_resolve_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_RESOLVE_OP_HPP +#define ASIO_DETAIL_WINRT_RESOLVE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_resolve_op : + public winrt_async_op< + Windows::Foundation::Collections::IVectorView< + Windows::Networking::EndpointPair^>^> +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_resolve_op); + + typedef typename Protocol::endpoint endpoint_type; + typedef asio::ip::basic_resolver_query query_type; + typedef asio::ip::basic_resolver_results results_type; + + winrt_resolve_op(const query_type& query, Handler& handler) + : winrt_async_op< + Windows::Foundation::Collections::IVectorView< + Windows::Networking::EndpointPair^>^>( + &winrt_resolve_op::do_complete), + query_(query), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_resolve_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + results_type results = results_type(); + if (!o->ec_) + { + try + { + results = results_type::create(o->result_, o->query_.hints(), + o->query_.host_name(), o->query_.service_name()); + } + catch (Platform::Exception^ e) + { + o->ec_ = asio::error_code(e->HResult, + asio::system_category()); + } + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, results); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + query_type query_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_RESOLVE_OP_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_resolver_service.hpp b/subprojects/asio/include/asio/detail/winrt_resolver_service.hpp new file mode 100644 index 000000000..aeb44485e --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_resolver_service.hpp @@ -0,0 +1,198 @@ +// +// detail/winrt_resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_RESOLVER_SERVICE_HPP +#define ASIO_DETAIL_WINRT_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/winrt_async_manager.hpp" +#include "asio/detail/winrt_resolve_op.hpp" +#include "asio/detail/winrt_utils.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_resolver_service : + public service_base > +{ +public: + // The implementation type of the resolver. A cancellation token is used to + // indicate to the asynchronous operation that the operation has been + // cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The query type. + typedef asio::ip::basic_resolver_query query_type; + + // The results type. + typedef asio::ip::basic_resolver_results results_type; + + // Constructor. + winrt_resolver_service(asio::io_context& io_context) + : service_base >(io_context), + io_context_(use_service(io_context)), + async_manager_(use_service(io_context)) + { + } + + // Destructor. + ~winrt_resolver_service() + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // Perform any fork-related housekeeping. + void notify_fork(asio::io_context::fork_event) + { + } + + // Construct a new resolver implementation. + void construct(implementation_type&) + { + } + + // Move-construct a new resolver implementation. + void move_construct(implementation_type&, + implementation_type&) + { + } + + // Move-assign from another resolver implementation. + void move_assign(implementation_type&, + winrt_resolver_service&, implementation_type&) + { + } + + // Destroy a resolver implementation. + void destroy(implementation_type&) + { + } + + // Cancel pending asynchronous operations. + void cancel(implementation_type&) + { + } + + // Resolve a query to a list of entries. + results_type resolve(implementation_type&, + const query_type& query, asio::error_code& ec) + { + try + { + using namespace Windows::Networking::Sockets; + auto endpoint_pairs = async_manager_.sync( + DatagramSocket::GetEndpointPairsAsync( + winrt_utils::host_name(query.host_name()), + winrt_utils::string(query.service_name())), ec); + + if (ec) + return results_type(); + + return results_type::create( + endpoint_pairs, query.hints(), + query.host_name(), query.service_name()); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return results_type(); + } + } + + // Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, + const query_type& query, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_resolve_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(query, handler); + + ASIO_HANDLER_CREATION((io_context_.context(), + *p.p, "resolver", &impl, 0, "async_resolve")); + (void)impl; + + try + { + using namespace Windows::Networking::Sockets; + async_manager_.async(DatagramSocket::GetEndpointPairsAsync( + winrt_utils::host_name(query.host_name()), + winrt_utils::string(query.service_name())), p.p); + p.v = p.p = 0; + } + catch (Platform::Exception^ e) + { + p.p->ec_ = asio::error_code( + e->HResult, asio::system_category()); + io_context_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + } + } + + // Resolve an endpoint to a list of entries. + results_type resolve(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return results_type(); + } + + // Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type&, + const endpoint_type&, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const results_type results; + io_context_.get_io_context().post( + detail::bind_handler(handler, ec, results)); + } + +private: + io_context_impl& io_context_; + winrt_async_manager& async_manager_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_RESOLVER_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_socket_connect_op.hpp b/subprojects/asio/include/asio/detail/winrt_socket_connect_op.hpp new file mode 100644 index 000000000..f18b445f8 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_socket_connect_op.hpp @@ -0,0 +1,92 @@ +// +// detail/winrt_socket_connect_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SOCKET_CONNECT_OP_HPP +#define ASIO_DETAIL_WINRT_SOCKET_CONNECT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_socket_connect_op : + public winrt_async_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_socket_connect_op); + + winrt_socket_connect_op(Handler& handler) + : winrt_async_op(&winrt_socket_connect_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_socket_connect_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SOCKET_CONNECT_OP_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_socket_recv_op.hpp b/subprojects/asio/include/asio/detail/winrt_socket_recv_op.hpp new file mode 100644 index 000000000..8f1fcf576 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_socket_recv_op.hpp @@ -0,0 +1,112 @@ +// +// detail/winrt_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_WINRT_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_socket_recv_op : + public winrt_async_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_socket_recv_op); + + winrt_socket_recv_op(const MutableBufferSequence& buffers, Handler& handler) + : winrt_async_op( + &winrt_socket_recv_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_socket_recv_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + std::size_t bytes_transferred = o->result_ ? o->result_->Length : 0; + if (bytes_transferred == 0 && !o->ec_ && + !buffer_sequence_adapter::all_empty(o->buffers_)) + { + o->ec_ = asio::error::eof; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SOCKET_RECV_OP_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_socket_send_op.hpp b/subprojects/asio/include/asio/detail/winrt_socket_send_op.hpp new file mode 100644 index 000000000..1148c24e4 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_socket_send_op.hpp @@ -0,0 +1,103 @@ +// +// detail/winrt_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_WINRT_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_socket_send_op : + public winrt_async_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_socket_send_op); + + winrt_socket_send_op(const ConstBufferSequence& buffers, Handler& handler) + : winrt_async_op(&winrt_socket_send_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + handler_work::start(handler_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_socket_send_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + handler_work w(o->handler_); + + ASIO_HANDLER_COMPLETION((*o)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->result_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + ConstBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SOCKET_SEND_OP_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_ssocket_service.hpp b/subprojects/asio/include/asio/detail/winrt_ssocket_service.hpp new file mode 100644 index 000000000..603724a30 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_ssocket_service.hpp @@ -0,0 +1,241 @@ +// +// detail/winrt_ssocket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SSOCKET_SERVICE_HPP +#define ASIO_DETAIL_WINRT_SSOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_socket_connect_op.hpp" +#include "asio/detail/winrt_ssocket_service_base.hpp" +#include "asio/detail/winrt_utils.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_ssocket_service : + public service_base >, + public winrt_ssocket_service_base +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef Windows::Networking::Sockets::StreamSocket^ native_handle_type; + + // The implementation type of the socket. + struct implementation_type : base_implementation_type + { + // Default constructor. + implementation_type() + : base_implementation_type(), + protocol_(endpoint_type().protocol()) + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // Constructor. + winrt_ssocket_service(asio::io_context& io_context) + : service_base >(io_context), + winrt_ssocket_service_base(io_context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + winrt_ssocket_service& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type& impl, + winrt_ssocket_service&, + typename winrt_ssocket_service< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + try + { + impl.socket_ = ref new Windows::Networking::Sockets::StreamSocket; + impl.protocol_ = protocol; + ec = asio::error_code(); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } + + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + impl.socket_ = native_socket; + impl.protocol_ = protocol; + ec = asio::error_code(); + + return ec; + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + endpoint.resize(do_get_endpoint(impl, true, + endpoint.data(), endpoint.size(), ec)); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + endpoint.resize(do_get_endpoint(impl, false, + endpoint.data(), endpoint.size(), ec)); + return endpoint; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + return do_set_option(impl, option.level(impl.protocol_), + option.name(impl.protocol_), option.data(impl.protocol_), + option.size(impl.protocol_), ec); + } + + // Get a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + do_get_option(impl, option.level(impl.protocol_), + option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + return do_connect(impl, peer_endpoint.data(), ec); + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_socket_connect_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler); + + ASIO_HANDLER_CREATION((io_context_.context(), + *p.p, "socket", &impl, 0, "async_connect")); + + start_connect_op(impl, peer_endpoint.data(), p.p, is_continuation); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SSOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_ssocket_service_base.hpp b/subprojects/asio/include/asio/detail/winrt_ssocket_service_base.hpp new file mode 100644 index 000000000..61328d0a0 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_ssocket_service_base.hpp @@ -0,0 +1,359 @@ +// +// detail/winrt_ssocket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SSOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_WINRT_SSOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winrt_async_manager.hpp" +#include "asio/detail/winrt_socket_recv_op.hpp" +#include "asio/detail/winrt_socket_send_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_ssocket_service_base +{ +public: + // The native type of a socket. + typedef Windows::Networking::Sockets::StreamSocket^ native_handle_type; + + // The implementation type of the socket. + struct base_implementation_type + { + // Default constructor. + base_implementation_type() + : socket_(nullptr), + next_(0), + prev_(0) + { + } + + // The underlying native socket. + native_handle_type socket_; + + // Pointers to adjacent socket implementations in linked list. + base_implementation_type* next_; + base_implementation_type* prev_; + }; + + // Constructor. + ASIO_DECL winrt_ssocket_service_base( + asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type&); + + // Move-construct a new socket implementation. + ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl); + + // Move-assign from another socket implementation. + ASIO_DECL void base_move_assign(base_implementation_type& impl, + winrt_ssocket_service_base& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != nullptr; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Release ownership of the socket. + ASIO_DECL native_handle_type release( + base_implementation_type& impl, asio::error_code& ec); + + // Get the native socket representation. + native_handle_type native_handle(base_implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(base_implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return false; + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type&, + IO_Control_Command&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(base_implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(base_implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(base_implementation_type&, + socket_base::shutdown_type, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Send the given data to the peer. + template + std::size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return do_send(impl, + buffer_sequence_adapter::first(buffers), flags, ec); + } + + // Wait until data can be sent without blocking. + std::size_t send(base_implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_socket_send_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler); + + ASIO_HANDLER_CREATION((io_context_.context(), + *p.p, "socket", &impl, 0, "async_send")); + + start_send_op(impl, + buffer_sequence_adapter::first(buffers), + flags, p.p, is_continuation); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.get_io_context().post( + detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Receive some data from the peer. Returns the number of bytes received. + template + std::size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return do_receive(impl, + buffer_sequence_adapter::first(buffers), flags, ec); + } + + // Wait until data can be received without blocking. + std::size_t receive(base_implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler& handler) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_socket_recv_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler); + + ASIO_HANDLER_CREATION((io_context_.context(), + *p.p, "socket", &impl, 0, "async_receive")); + + start_receive_op(impl, + buffer_sequence_adapter::first(buffers), + flags, p.p, is_continuation); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + io_context_.get_io_context().post( + detail::bind_handler(handler, ec, bytes_transferred)); + } + +protected: + // Helper function to obtain endpoints associated with the connection. + ASIO_DECL std::size_t do_get_endpoint( + const base_implementation_type& impl, bool local, + void* addr, std::size_t addr_len, asio::error_code& ec) const; + + // Helper function to set a socket option. + ASIO_DECL asio::error_code do_set_option( + base_implementation_type& impl, + int level, int optname, const void* optval, + std::size_t optlen, asio::error_code& ec); + + // Helper function to get a socket option. + ASIO_DECL void do_get_option( + const base_implementation_type& impl, + int level, int optname, void* optval, + std::size_t* optlen, asio::error_code& ec) const; + + // Helper function to perform a synchronous connect. + ASIO_DECL asio::error_code do_connect( + base_implementation_type& impl, + const void* addr, asio::error_code& ec); + + // Helper function to start an asynchronous connect. + ASIO_DECL void start_connect_op( + base_implementation_type& impl, const void* addr, + winrt_async_op* op, bool is_continuation); + + // Helper function to perform a synchronous send. + ASIO_DECL std::size_t do_send( + base_implementation_type& impl, const asio::const_buffer& data, + socket_base::message_flags flags, asio::error_code& ec); + + // Helper function to start an asynchronous send. + ASIO_DECL void start_send_op(base_implementation_type& impl, + const asio::const_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, bool is_continuation); + + // Helper function to perform a synchronous receive. + ASIO_DECL std::size_t do_receive( + base_implementation_type& impl, const asio::mutable_buffer& data, + socket_base::message_flags flags, asio::error_code& ec); + + // Helper function to start an asynchronous receive. + ASIO_DECL void start_receive_op(base_implementation_type& impl, + const asio::mutable_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, + bool is_continuation); + + // The io_context implementation used for delivering completions. + io_context_impl& io_context_; + + // The manager that keeps track of outstanding operations. + winrt_async_manager& async_manager_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + base_implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/winrt_ssocket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SSOCKET_SERVICE_BASE_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_timer_scheduler.hpp b/subprojects/asio/include/asio/detail/winrt_timer_scheduler.hpp new file mode 100644 index 000000000..5dec7c0b6 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_timer_scheduler.hpp @@ -0,0 +1,137 @@ +// +// detail/winrt_timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_TIMER_SCHEDULER_HPP +#define ASIO_DETAIL_WINRT_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/event.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/thread.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_timer_scheduler + : public asio::detail::service_base +{ +public: + // Constructor. + ASIO_DECL winrt_timer_scheduler(asio::io_context& io_context); + + // Destructor. + ASIO_DECL ~winrt_timer_scheduler(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::io_context::fork_event fork_ev); + + // Initialise the task. No effect as this class uses its own thread. + ASIO_DECL void init_task(); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from); + +private: + // Run the select loop in the thread. + ASIO_DECL void run_thread(); + + // Entry point for the select loop thread. + ASIO_DECL static void call_run_thread(winrt_timer_scheduler* reactor); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // The io_context implementation used to post completions. + io_context_impl& io_context_; + + // Mutex used to protect internal variables. + asio::detail::mutex mutex_; + + // Event used to wake up background thread. + asio::detail::event event_; + + // The timer queues. + timer_queue_set timer_queues_; + + // The background thread that is waiting for timers to expire. + asio::detail::thread* thread_; + + // Does the background thread need to stop. + bool stop_thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/winrt_timer_scheduler.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/winrt_timer_scheduler.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_TIMER_SCHEDULER_HPP diff --git a/subprojects/asio/include/asio/detail/winrt_utils.hpp b/subprojects/asio/include/asio/detail/winrt_utils.hpp new file mode 100644 index 000000000..22a248958 --- /dev/null +++ b/subprojects/asio/include/asio/detail/winrt_utils.hpp @@ -0,0 +1,106 @@ +// +// detail/winrt_utils.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_UTILS_HPP +#define ASIO_DETAIL_WINRT_UTILS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include +#include +#include +#include +#include +#include +#include "asio/buffer.hpp" +#include "asio/error_code.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace winrt_utils { + +inline Platform::String^ string(const char* from) +{ + std::wstring tmp(from, from + std::strlen(from)); + return ref new Platform::String(tmp.c_str()); +} + +inline Platform::String^ string(const std::string& from) +{ + std::wstring tmp(from.begin(), from.end()); + return ref new Platform::String(tmp.c_str()); +} + +inline std::string string(Platform::String^ from) +{ + std::wstring_convert> converter; + return converter.to_bytes(from->Data()); +} + +inline Platform::String^ string(unsigned short from) +{ + return string(std::to_string(from)); +} + +template +inline Platform::String^ string(const T& from) +{ + return string(from.to_string()); +} + +inline int integer(Platform::String^ from) +{ + return _wtoi(from->Data()); +} + +template +inline Windows::Networking::HostName^ host_name(const T& from) +{ + return ref new Windows::Networking::HostName((string)(from)); +} + +template +inline Windows::Storage::Streams::IBuffer^ buffer_dup( + const ConstBufferSequence& buffers) +{ + using Microsoft::WRL::ComPtr; + using asio::buffer_size; + std::size_t size = buffer_size(buffers); + auto b = ref new Windows::Storage::Streams::Buffer(size); + ComPtr insp = reinterpret_cast(b); + ComPtr bacc; + insp.As(&bacc); + byte* bytes = nullptr; + bacc->Buffer(&bytes); + asio::buffer_copy(asio::buffer(bytes, size), buffers); + b->Length = size; + return b; +} + +} // namespace winrt_utils +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_UTILS_HPP diff --git a/subprojects/asio/include/asio/detail/winsock_init.hpp b/subprojects/asio/include/asio/detail/winsock_init.hpp new file mode 100644 index 000000000..9094363ab --- /dev/null +++ b/subprojects/asio/include/asio/detail/winsock_init.hpp @@ -0,0 +1,128 @@ +// +// detail/winsock_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINSOCK_INIT_HPP +#define ASIO_DETAIL_WINSOCK_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winsock_init_base +{ +protected: + // Structure to track result of initialisation and number of uses. POD is used + // to ensure that the values are zero-initialised prior to any code being run. + struct data + { + long init_count_; + long result_; + }; + + ASIO_DECL static void startup(data& d, + unsigned char major, unsigned char minor); + + ASIO_DECL static void manual_startup(data& d); + + ASIO_DECL static void cleanup(data& d); + + ASIO_DECL static void manual_cleanup(data& d); + + ASIO_DECL static void throw_on_error(data& d); +}; + +template +class winsock_init : private winsock_init_base +{ +public: + winsock_init(bool allow_throw = true) + { + startup(data_, Major, Minor); + if (allow_throw) + throw_on_error(data_); + } + + winsock_init(const winsock_init&) + { + startup(data_, Major, Minor); + throw_on_error(data_); + } + + ~winsock_init() + { + cleanup(data_); + } + + // This class may be used to indicate that user code will manage Winsock + // initialisation and cleanup. This may be required in the case of a DLL, for + // example, where it is not safe to initialise Winsock from global object + // constructors. + // + // To prevent asio from initialising Winsock, the object must be constructed + // before any Asio's own global objects. With MSVC, this may be accomplished + // by adding the following code to the DLL: + // + // #pragma warning(push) + // #pragma warning(disable:4073) + // #pragma init_seg(lib) + // asio::detail::winsock_init<>::manual manual_winsock_init; + // #pragma warning(pop) + class manual + { + public: + manual() + { + manual_startup(data_); + } + + manual(const manual&) + { + manual_startup(data_); + } + + ~manual() + { + manual_cleanup(data_); + } + }; + +private: + friend class manual; + static data data_; +}; + +template +winsock_init_base::data winsock_init::data_; + +// Static variable to ensure that winsock is initialised before main, and +// therefore before any other threads can get started. +static const winsock_init<>& winsock_init_instance = winsock_init<>(false); + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/winsock_init.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_WINSOCK_INIT_HPP diff --git a/subprojects/asio/include/asio/detail/work_dispatcher.hpp b/subprojects/asio/include/asio/detail/work_dispatcher.hpp new file mode 100644 index 000000000..7abce497b --- /dev/null +++ b/subprojects/asio/include/asio/detail/work_dispatcher.hpp @@ -0,0 +1,72 @@ +// +// detail/work_dispatcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WORK_DISPATCHER_HPP +#define ASIO_DETAIL_WORK_DISPATCHER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_executor.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/executor_work_guard.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class work_dispatcher +{ +public: + work_dispatcher(Handler& handler) + : work_((get_associated_executor)(handler)), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + work_dispatcher(const work_dispatcher& other) + : work_(other.work_), + handler_(other.handler_) + { + } + + work_dispatcher(work_dispatcher&& other) + : work_(ASIO_MOVE_CAST(executor_work_guard< + typename associated_executor::type>)(other.work_)), + handler_(ASIO_MOVE_CAST(Handler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + typename associated_allocator::type alloc( + (get_associated_allocator)(handler_)); + work_.get_executor().dispatch( + ASIO_MOVE_CAST(Handler)(handler_), alloc); + work_.reset(); + } + +private: + executor_work_guard::type> work_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WORK_DISPATCHER_HPP diff --git a/subprojects/asio/include/asio/detail/wrapped_handler.hpp b/subprojects/asio/include/asio/detail/wrapped_handler.hpp new file mode 100644 index 000000000..751f0ea83 --- /dev/null +++ b/subprojects/asio/include/asio/detail/wrapped_handler.hpp @@ -0,0 +1,291 @@ +// +// detail/wrapped_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WRAPPED_HANDLER_HPP +#define ASIO_DETAIL_WRAPPED_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct is_continuation_delegated +{ + template + bool operator()(Dispatcher&, Handler& handler) const + { + return asio_handler_cont_helpers::is_continuation(handler); + } +}; + +struct is_continuation_if_running +{ + template + bool operator()(Dispatcher& dispatcher, Handler&) const + { + return dispatcher.running_in_this_thread(); + } +}; + +template +class wrapped_handler +{ +public: + typedef void result_type; + + wrapped_handler(Dispatcher dispatcher, Handler& handler) + : dispatcher_(dispatcher), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + wrapped_handler(const wrapped_handler& other) + : dispatcher_(other.dispatcher_), + handler_(other.handler_) + { + } + + wrapped_handler(wrapped_handler&& other) + : dispatcher_(other.dispatcher_), + handler_(ASIO_MOVE_CAST(Handler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + dispatcher_.dispatch(ASIO_MOVE_CAST(Handler)(handler_)); + } + + void operator()() const + { + dispatcher_.dispatch(handler_); + } + + template + void operator()(const Arg1& arg1) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + +//private: + Dispatcher dispatcher_; + Handler handler_; +}; + +template +class rewrapped_handler +{ +public: + explicit rewrapped_handler(Handler& handler, const Context& context) + : context_(context), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + } + + explicit rewrapped_handler(const Handler& handler, const Context& context) + : context_(context), + handler_(handler) + { + } + +#if defined(ASIO_HAS_MOVE) + rewrapped_handler(const rewrapped_handler& other) + : context_(other.context_), + handler_(other.handler_) + { + } + + rewrapped_handler(rewrapped_handler&& other) + : context_(ASIO_MOVE_CAST(Context)(other.context_)), + handler_(ASIO_MOVE_CAST(Handler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(); + } + + void operator()() const + { + handler_(); + } + +//private: + Context context_; + Handler handler_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + wrapped_handler* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + wrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + wrapped_handler* this_handler) +{ + return IsContinuation()(this_handler->dispatcher_, this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + wrapped_handler* this_handler) +{ + this_handler->dispatcher_.dispatch( + rewrapped_handler( + function, this_handler->handler_)); +} + +template +inline void asio_handler_invoke(const Function& function, + wrapped_handler* this_handler) +{ + this_handler->dispatcher_.dispatch( + rewrapped_handler( + function, this_handler->handler_)); +} + +template +inline void* asio_handler_allocate(std::size_t size, + rewrapped_handler* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->context_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + rewrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->context_); +} + +template +inline bool asio_handler_is_continuation( + rewrapped_handler* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->context_); +} + +template +inline void asio_handler_invoke(Function& function, + rewrapped_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->context_); +} + +template +inline void asio_handler_invoke(const Function& function, + rewrapped_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->context_); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WRAPPED_HANDLER_HPP diff --git a/subprojects/asio/include/asio/dispatch.hpp b/subprojects/asio/include/asio/dispatch.hpp new file mode 100644 index 000000000..b8dfd6910 --- /dev/null +++ b/subprojects/asio/include/asio/dispatch.hpp @@ -0,0 +1,108 @@ +// +// dispatch.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DISPATCH_HPP +#define ASIO_DISPATCH_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the object's associated + * executor. The function object is queued for execution, and is never called + * from the current thread prior to returning from dispatch(). + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex by performing + * get_associated_executor(handler). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Performs ex.dispatch(std::move(handler), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) dispatch( + ASIO_MOVE_ARG(CompletionToken) token); + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the specified executor. + * The function object is queued for execution, and is never called from the + * current thread prior to returning from dispatch(). + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex1 by performing + * get_associated_executor(handler). + * + * @li Creates a work object @c w by performing make_work(ex1). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Constructs a function object @c f with a function call operator that + * performs ex1.dispatch(std::move(handler), alloc) followed by + * w.reset(). + * + * @li Performs Executor(ex).dispatch(std::move(f), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) dispatch( + const Executor& ex, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type* = 0); + +/// Submits a completion token or function object for execution. +/** + * @returns dispatch(ctx.get_executor(), + * forward(token)). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) dispatch( + ExecutionContext& ctx, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type* = 0); + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/dispatch.hpp" + +#endif // ASIO_DISPATCH_HPP diff --git a/subprojects/asio/include/asio/error.hpp b/subprojects/asio/include/asio/error.hpp new file mode 100644 index 000000000..2be9bdeed --- /dev/null +++ b/subprojects/asio/include/asio/error.hpp @@ -0,0 +1,356 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_HPP +#define ASIO_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error_code.hpp" +#include "asio/system_error.hpp" +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(ASIO_WINDOWS_RUNTIME) +# include +#else +# include +# include +#endif + +#if defined(GENERATING_DOCUMENTATION) +/// INTERNAL ONLY. +# define ASIO_NATIVE_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_SOCKET_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_NETDB_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_GETADDRINFO_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined +#elif defined(ASIO_WINDOWS_RUNTIME) +# define ASIO_NATIVE_ERROR(e) __HRESULT_FROM_WIN32(e) +# define ASIO_SOCKET_ERROR(e) __HRESULT_FROM_WIN32(WSA ## e) +# define ASIO_NETDB_ERROR(e) __HRESULT_FROM_WIN32(WSA ## e) +# define ASIO_GETADDRINFO_ERROR(e) __HRESULT_FROM_WIN32(WSA ## e) +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# define ASIO_NATIVE_ERROR(e) e +# define ASIO_SOCKET_ERROR(e) WSA ## e +# define ASIO_NETDB_ERROR(e) WSA ## e +# define ASIO_GETADDRINFO_ERROR(e) WSA ## e +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win +#else +# define ASIO_NATIVE_ERROR(e) e +# define ASIO_SOCKET_ERROR(e) e +# define ASIO_NETDB_ERROR(e) e +# define ASIO_GETADDRINFO_ERROR(e) e +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace error { + +enum basic_errors +{ + /// Permission denied. + access_denied = ASIO_SOCKET_ERROR(EACCES), + + /// Address family not supported by protocol. + address_family_not_supported = ASIO_SOCKET_ERROR(EAFNOSUPPORT), + + /// Address already in use. + address_in_use = ASIO_SOCKET_ERROR(EADDRINUSE), + + /// Transport endpoint is already connected. + already_connected = ASIO_SOCKET_ERROR(EISCONN), + + /// Operation already in progress. + already_started = ASIO_SOCKET_ERROR(EALREADY), + + /// Broken pipe. + broken_pipe = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_BROKEN_PIPE), + ASIO_NATIVE_ERROR(EPIPE)), + + /// A connection has been aborted. + connection_aborted = ASIO_SOCKET_ERROR(ECONNABORTED), + + /// Connection refused. + connection_refused = ASIO_SOCKET_ERROR(ECONNREFUSED), + + /// Connection reset by peer. + connection_reset = ASIO_SOCKET_ERROR(ECONNRESET), + + /// Bad file descriptor. + bad_descriptor = ASIO_SOCKET_ERROR(EBADF), + + /// Bad address. + fault = ASIO_SOCKET_ERROR(EFAULT), + + /// No route to host. + host_unreachable = ASIO_SOCKET_ERROR(EHOSTUNREACH), + + /// Operation now in progress. + in_progress = ASIO_SOCKET_ERROR(EINPROGRESS), + + /// Interrupted system call. + interrupted = ASIO_SOCKET_ERROR(EINTR), + + /// Invalid argument. + invalid_argument = ASIO_SOCKET_ERROR(EINVAL), + + /// Message too long. + message_size = ASIO_SOCKET_ERROR(EMSGSIZE), + + /// The name was too long. + name_too_long = ASIO_SOCKET_ERROR(ENAMETOOLONG), + + /// Network is down. + network_down = ASIO_SOCKET_ERROR(ENETDOWN), + + /// Network dropped connection on reset. + network_reset = ASIO_SOCKET_ERROR(ENETRESET), + + /// Network is unreachable. + network_unreachable = ASIO_SOCKET_ERROR(ENETUNREACH), + + /// Too many open files. + no_descriptors = ASIO_SOCKET_ERROR(EMFILE), + + /// No buffer space available. + no_buffer_space = ASIO_SOCKET_ERROR(ENOBUFS), + + /// Cannot allocate memory. + no_memory = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_OUTOFMEMORY), + ASIO_NATIVE_ERROR(ENOMEM)), + + /// Operation not permitted. + no_permission = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_ACCESS_DENIED), + ASIO_NATIVE_ERROR(EPERM)), + + /// Protocol not available. + no_protocol_option = ASIO_SOCKET_ERROR(ENOPROTOOPT), + + /// No such device. + no_such_device = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_BAD_UNIT), + ASIO_NATIVE_ERROR(ENODEV)), + + /// Transport endpoint is not connected. + not_connected = ASIO_SOCKET_ERROR(ENOTCONN), + + /// Socket operation on non-socket. + not_socket = ASIO_SOCKET_ERROR(ENOTSOCK), + + /// Operation cancelled. + operation_aborted = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_OPERATION_ABORTED), + ASIO_NATIVE_ERROR(ECANCELED)), + + /// Operation not supported. + operation_not_supported = ASIO_SOCKET_ERROR(EOPNOTSUPP), + + /// Cannot send after transport endpoint shutdown. + shut_down = ASIO_SOCKET_ERROR(ESHUTDOWN), + + /// Connection timed out. + timed_out = ASIO_SOCKET_ERROR(ETIMEDOUT), + + /// Resource temporarily unavailable. + try_again = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_RETRY), + ASIO_NATIVE_ERROR(EAGAIN)), + + /// The socket is marked non-blocking and the requested operation would block. + would_block = ASIO_SOCKET_ERROR(EWOULDBLOCK) +}; + +enum netdb_errors +{ + /// Host not found (authoritative). + host_not_found = ASIO_NETDB_ERROR(HOST_NOT_FOUND), + + /// Host not found (non-authoritative). + host_not_found_try_again = ASIO_NETDB_ERROR(TRY_AGAIN), + + /// The query is valid but does not have associated address data. + no_data = ASIO_NETDB_ERROR(NO_DATA), + + /// A non-recoverable error occurred. + no_recovery = ASIO_NETDB_ERROR(NO_RECOVERY) +}; + +enum addrinfo_errors +{ + /// The service is not supported for the given socket type. + service_not_found = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(WSATYPE_NOT_FOUND), + ASIO_GETADDRINFO_ERROR(EAI_SERVICE)), + + /// The socket type is not supported. + socket_type_not_supported = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(WSAESOCKTNOSUPPORT), + ASIO_GETADDRINFO_ERROR(EAI_SOCKTYPE)) +}; + +enum misc_errors +{ + /// Already open. + already_open = 1, + + /// End of file or stream. + eof, + + /// Element not found. + not_found, + + /// The descriptor cannot fit into the select system call's fd_set. + fd_set_failure +}; + +inline const asio::error_category& get_system_category() +{ + return asio::system_category(); +} + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +extern ASIO_DECL +const asio::error_category& get_netdb_category(); + +extern ASIO_DECL +const asio::error_category& get_addrinfo_category(); + +#else // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +inline const asio::error_category& get_netdb_category() +{ + return get_system_category(); +} + +inline const asio::error_category& get_addrinfo_category() +{ + return get_system_category(); +} + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +extern ASIO_DECL +const asio::error_category& get_misc_category(); + +static const asio::error_category& + system_category ASIO_UNUSED_VARIABLE + = asio::error::get_system_category(); +static const asio::error_category& + netdb_category ASIO_UNUSED_VARIABLE + = asio::error::get_netdb_category(); +static const asio::error_category& + addrinfo_category ASIO_UNUSED_VARIABLE + = asio::error::get_addrinfo_category(); +static const asio::error_category& + misc_category ASIO_UNUSED_VARIABLE + = asio::error::get_misc_category(); + +} // namespace error +} // namespace asio + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) +namespace std { + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +} // namespace std +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +namespace asio { +namespace error { + +inline asio::error_code make_error_code(basic_errors e) +{ + return asio::error_code( + static_cast(e), get_system_category()); +} + +inline asio::error_code make_error_code(netdb_errors e) +{ + return asio::error_code( + static_cast(e), get_netdb_category()); +} + +inline asio::error_code make_error_code(addrinfo_errors e) +{ + return asio::error_code( + static_cast(e), get_addrinfo_category()); +} + +inline asio::error_code make_error_code(misc_errors e) +{ + return asio::error_code( + static_cast(e), get_misc_category()); +} + +} // namespace error +namespace stream_errc { + // Simulates the proposed stream_errc scoped enum. + using error::eof; + using error::not_found; +} // namespace stream_errc +namespace socket_errc { + // Simulates the proposed socket_errc scoped enum. + using error::already_open; + using error::not_found; +} // namespace socket_errc +namespace resolver_errc { + // Simulates the proposed resolver_errc scoped enum. + using error::host_not_found; + const error::netdb_errors try_again = error::host_not_found_try_again; + using error::service_not_found; +} // namespace resolver_errc +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#undef ASIO_NATIVE_ERROR +#undef ASIO_SOCKET_ERROR +#undef ASIO_NETDB_ERROR +#undef ASIO_GETADDRINFO_ERROR +#undef ASIO_WIN_OR_POSIX + +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/error.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_ERROR_HPP diff --git a/subprojects/asio/include/asio/error_code.hpp b/subprojects/asio/include/asio/error_code.hpp new file mode 100644 index 000000000..7c239e2fa --- /dev/null +++ b/subprojects/asio/include/asio/error_code.hpp @@ -0,0 +1,202 @@ +// +// error_code.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_CODE_HPP +#define ASIO_ERROR_CODE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) +# include +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) +# include +# include "asio/detail/noncopyable.hpp" +# if !defined(ASIO_NO_IOSTREAM) +# include +# endif // !defined(ASIO_NO_IOSTREAM) +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) + +typedef std::error_category error_category; + +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +/// Base class for all error categories. +class error_category : private noncopyable +{ +public: + /// Destructor. + virtual ~error_category() + { + } + + /// Returns a string naming the error gategory. + virtual const char* name() const = 0; + + /// Returns a string describing the error denoted by @c value. + virtual std::string message(int value) const = 0; + + /// Equality operator to compare two error categories. + bool operator==(const error_category& rhs) const + { + return this == &rhs; + } + + /// Inequality operator to compare two error categories. + bool operator!=(const error_category& rhs) const + { + return !(*this == rhs); + } +}; + +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +/// Returns the error category used for the system errors produced by asio. +extern ASIO_DECL const error_category& system_category(); + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) + +typedef std::error_code error_code; + +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +/// Class to represent an error code value. +class error_code +{ +public: + /// Default constructor. + error_code() + : value_(0), + category_(&system_category()) + { + } + + /// Construct with specific error code and category. + error_code(int v, const error_category& c) + : value_(v), + category_(&c) + { + } + + /// Construct from an error code enum. + template + error_code(ErrorEnum e) + { + *this = make_error_code(e); + } + + /// Clear the error value to the default. + void clear() + { + value_ = 0; + category_ = &system_category(); + } + + /// Assign a new error value. + void assign(int v, const error_category& c) + { + value_ = v; + category_ = &c; + } + + /// Get the error value. + int value() const + { + return value_; + } + + /// Get the error category. + const error_category& category() const + { + return *category_; + } + + /// Get the message associated with the error. + std::string message() const + { + return category_->message(value_); + } + + struct unspecified_bool_type_t + { + }; + + typedef void (*unspecified_bool_type)(unspecified_bool_type_t); + + static void unspecified_bool_true(unspecified_bool_type_t) {} + + /// Operator returns non-null if there is a non-success error code. + operator unspecified_bool_type() const + { + if (value_ == 0) + return 0; + else + return &error_code::unspecified_bool_true; + } + + /// Operator to test if the error represents success. + bool operator!() const + { + return value_ == 0; + } + + /// Equality operator to compare two error objects. + friend bool operator==(const error_code& e1, const error_code& e2) + { + return e1.value_ == e2.value_ && e1.category_ == e2.category_; + } + + /// Inequality operator to compare two error objects. + friend bool operator!=(const error_code& e1, const error_code& e2) + { + return e1.value_ != e2.value_ || e1.category_ != e2.category_; + } + +private: + // The value associated with the error code. + int value_; + + // The category associated with the error code. + const error_category* category_; +}; + +# if !defined(ASIO_NO_IOSTREAM) + +/// Output an error code. +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const error_code& ec) +{ + os << ec.category().name() << ':' << ec.value(); + return os; +} + +# endif // !defined(ASIO_NO_IOSTREAM) + +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/error_code.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_ERROR_CODE_HPP diff --git a/subprojects/asio/include/asio/execution_context.hpp b/subprojects/asio/include/asio/execution_context.hpp new file mode 100644 index 000000000..1476d19b5 --- /dev/null +++ b/subprojects/asio/include/asio/execution_context.hpp @@ -0,0 +1,411 @@ +// +// execution_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_CONTEXT_HPP +#define ASIO_EXECUTION_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/variadic_templates.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class execution_context; +class io_context; + +#if !defined(GENERATING_DOCUMENTATION) +template Service& use_service(execution_context&); +template Service& use_service(io_context&); +template void add_service(execution_context&, Service*); +template bool has_service(execution_context&); +#endif // !defined(GENERATING_DOCUMENTATION) + +namespace detail { class service_registry; } + +/// A context for function object execution. +/** + * An execution context represents a place where function objects will be + * executed. An @c io_context is an example of an execution context. + * + * @par The execution_context class and services + * + * Class execution_context implements an extensible, type-safe, polymorphic set + * of services, indexed by service type. + * + * Services exist to manage the resources that are shared across an execution + * context. For example, timers may be implemented in terms of a single timer + * queue, and this queue would be stored in a service. + * + * Access to the services of an execution_context is via three function + * templates, use_service(), add_service() and has_service(). + * + * In a call to @c use_service(), the type argument chooses a service, + * making available all members of the named type. If @c Service is not present + * in an execution_context, an object of type @c Service is created and added + * to the execution_context. A C++ program can check if an execution_context + * implements a particular service with the function template @c + * has_service(). + * + * Service objects may be explicitly added to an execution_context using the + * function template @c add_service(). If the @c Service is already + * present, the service_already_exists exception is thrown. If the owner of the + * service is not the same object as the execution_context parameter, the + * invalid_service_owner exception is thrown. + * + * Once a service reference is obtained from an execution_context object by + * calling use_service(), that reference remains usable as long as the owning + * execution_context object exists. + * + * All service implementations have execution_context::service as a public base + * class. Custom services may be implemented by deriving from this class and + * then added to an execution_context using the facilities described above. + * + * @par The execution_context as a base class + * + * Class execution_context may be used only as a base class for concrete + * execution context types. The @c io_context is an example of such a derived + * type. + * + * On destruction, a class that is derived from execution_context must perform + * execution_context::shutdown() followed by + * execution_context::destroy(). + * + * This destruction sequence permits programs to simplify their resource + * management by using @c shared_ptr<>. Where an object's lifetime is tied to + * the lifetime of a connection (or some other sequence of asynchronous + * operations), a @c shared_ptr to the object would be bound into the handlers + * for all asynchronous operations associated with it. This works as follows: + * + * @li When a single connection ends, all associated asynchronous operations + * complete. The corresponding handler objects are destroyed, and all @c + * shared_ptr references to the objects are destroyed. + * + * @li To shut down the whole program, the io_context function stop() is called + * to terminate any run() calls as soon as possible. The io_context destructor + * calls @c shutdown() and @c destroy() to destroy all pending handlers, + * causing all @c shared_ptr references to all connection objects to be + * destroyed. + */ +class execution_context + : private noncopyable +{ +public: + class id; + class service; + +protected: + /// Constructor. + ASIO_DECL execution_context(); + + /// Destructor. + ASIO_DECL ~execution_context(); + + /// Shuts down all services in the context. + /** + * This function is implemented as follows: + * + * @li For each service object @c svc in the execution_context set, in + * reverse order of the beginning of service object lifetime, performs @c + * svc->shutdown(). + */ + ASIO_DECL void shutdown(); + + /// Destroys all services in the context. + /** + * This function is implemented as follows: + * + * @li For each service object @c svc in the execution_context set, in + * reverse order * of the beginning of service object lifetime, performs + * delete static_cast(svc). + */ + ASIO_DECL void destroy(); + +public: + /// Fork-related event notifications. + enum fork_event + { + /// Notify the context that the process is about to fork. + fork_prepare, + + /// Notify the context that the process has forked and is the parent. + fork_parent, + + /// Notify the context that the process has forked and is the child. + fork_child + }; + + /// Notify the execution_context of a fork-related event. + /** + * This function is used to inform the execution_context that the process is + * about to fork, or has just forked. This allows the execution_context, and + * the services it contains, to perform any necessary housekeeping to ensure + * correct operation following a fork. + * + * This function must not be called while any other execution_context + * function, or any function associated with the execution_context's derived + * class, is being called in another thread. It is, however, safe to call + * this function from within a completion handler, provided no other thread + * is accessing the execution_context or its derived class. + * + * @param event A fork-related event. + * + * @throws asio::system_error Thrown on failure. If the notification + * fails the execution_context object should no longer be used and should be + * destroyed. + * + * @par Example + * The following code illustrates how to incorporate the notify_fork() + * function: + * @code my_execution_context.notify_fork(execution_context::fork_prepare); + * if (fork() == 0) + * { + * // This is the child process. + * my_execution_context.notify_fork(execution_context::fork_child); + * } + * else + * { + * // This is the parent process. + * my_execution_context.notify_fork(execution_context::fork_parent); + * } @endcode + * + * @note For each service object @c svc in the execution_context set, + * performs svc->notify_fork();. When processing the fork_prepare + * event, services are visited in reverse order of the beginning of service + * object lifetime. Otherwise, services are visited in order of the beginning + * of service object lifetime. + */ + ASIO_DECL void notify_fork(fork_event event); + + /// Obtain the service object corresponding to the given type. + /** + * This function is used to locate a service object that corresponds to the + * given service type. If there is no existing implementation of the service, + * then the execution_context will create a new instance of the service. + * + * @param e The execution_context object that owns the service. + * + * @return The service interface implementing the specified service type. + * Ownership of the service interface is not transferred to the caller. + */ + template + friend Service& use_service(execution_context& e); + + /// Obtain the service object corresponding to the given type. + /** + * This function is used to locate a service object that corresponds to the + * given service type. If there is no existing implementation of the service, + * then the io_context will create a new instance of the service. + * + * @param ioc The io_context object that owns the service. + * + * @return The service interface implementing the specified service type. + * Ownership of the service interface is not transferred to the caller. + * + * @note This overload is preserved for backwards compatibility with services + * that inherit from io_context::service. + */ + template + friend Service& use_service(io_context& ioc); + +#if defined(GENERATING_DOCUMENTATION) + + /// Creates a service object and adds it to the execution_context. + /** + * This function is used to add a service to the execution_context. + * + * @param e The execution_context object that owns the service. + * + * @param args Zero or more arguments to be passed to the service + * constructor. + * + * @throws asio::service_already_exists Thrown if a service of the + * given type is already present in the execution_context. + */ + template + friend Service& make_service(execution_context& e, Args&&... args); + +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + friend Service& make_service(execution_context& e, + ASIO_MOVE_ARG(Args)... args); + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + friend Service& make_service(execution_context& e); + +#define ASIO_PRIVATE_MAKE_SERVICE_DEF(n) \ + template \ + friend Service& make_service(execution_context& e, \ + ASIO_VARIADIC_MOVE_PARAMS(n)); \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_MAKE_SERVICE_DEF) +#undef ASIO_PRIVATE_MAKE_SERVICE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + /// (Deprecated: Use make_service().) Add a service object to the + /// execution_context. + /** + * This function is used to add a service to the execution_context. + * + * @param e The execution_context object that owns the service. + * + * @param svc The service object. On success, ownership of the service object + * is transferred to the execution_context. When the execution_context object + * is destroyed, it will destroy the service object by performing: @code + * delete static_cast(svc) @endcode + * + * @throws asio::service_already_exists Thrown if a service of the + * given type is already present in the execution_context. + * + * @throws asio::invalid_service_owner Thrown if the service's owning + * execution_context is not the execution_context object specified by the + * @c e parameter. + */ + template + friend void add_service(execution_context& e, Service* svc); + + /// Determine if an execution_context contains a specified service type. + /** + * This function is used to determine whether the execution_context contains a + * service object corresponding to the given service type. + * + * @param e The execution_context object that owns the service. + * + * @return A boolean indicating whether the execution_context contains the + * service. + */ + template + friend bool has_service(execution_context& e); + +private: + // The service registry. + asio::detail::service_registry* service_registry_; +}; + +/// Class used to uniquely identify a service. +class execution_context::id + : private noncopyable +{ +public: + /// Constructor. + id() {} +}; + +/// Base class for all io_context services. +class execution_context::service + : private noncopyable +{ +public: + /// Get the context object that owns the service. + execution_context& context(); + +protected: + /// Constructor. + /** + * @param owner The execution_context object that owns the service. + */ + ASIO_DECL service(execution_context& owner); + + /// Destructor. + ASIO_DECL virtual ~service(); + +private: + /// Destroy all user-defined handler objects owned by the service. + virtual void shutdown() = 0; + + /// Handle notification of a fork-related event to perform any necessary + /// housekeeping. + /** + * This function is not a pure virtual so that services only have to + * implement it if necessary. The default implementation does nothing. + */ + ASIO_DECL virtual void notify_fork( + execution_context::fork_event event); + + friend class asio::detail::service_registry; + struct key + { + key() : type_info_(0), id_(0) {} + const std::type_info* type_info_; + const execution_context::id* id_; + } key_; + + execution_context& owner_; + service* next_; +}; + +/// Exception thrown when trying to add a duplicate service to an +/// execution_context. +class service_already_exists + : public std::logic_error +{ +public: + ASIO_DECL service_already_exists(); +}; + +/// Exception thrown when trying to add a service object to an +/// execution_context where the service has a different owner. +class invalid_service_owner + : public std::logic_error +{ +public: + ASIO_DECL invalid_service_owner(); +}; + +namespace detail { + +// Special derived service id type to keep classes header-file only. +template +class service_id + : public execution_context::id +{ +}; + +// Special service base class to keep classes header-file only. +template +class execution_context_service_base + : public execution_context::service +{ +public: + static service_id id; + + // Constructor. + execution_context_service_base(execution_context& e) + : execution_context::service(e) + { + } +}; + +template +service_id execution_context_service_base::id; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/execution_context.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/execution_context.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_EXECUTION_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/executor.hpp b/subprojects/asio/include/asio/executor.hpp new file mode 100644 index 000000000..e552cfbf2 --- /dev/null +++ b/subprojects/asio/include/asio/executor.hpp @@ -0,0 +1,341 @@ +// +// executor.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTOR_HPP +#define ASIO_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/cstddef.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Exception thrown when trying to access an empty polymorphic executor. +class bad_executor + : public std::exception +{ +public: + /// Constructor. + ASIO_DECL bad_executor() ASIO_NOEXCEPT; + + /// Obtain message associated with exception. + ASIO_DECL virtual const char* what() const + ASIO_NOEXCEPT_OR_NOTHROW; +}; + +/// Polymorphic wrapper for executors. +class executor +{ +public: + /// Default constructor. + executor() ASIO_NOEXCEPT + : impl_(0) + { + } + + /// Construct from nullptr. + executor(nullptr_t) ASIO_NOEXCEPT + : impl_(0) + { + } + + /// Copy constructor. + executor(const executor& other) ASIO_NOEXCEPT + : impl_(other.clone()) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move constructor. + executor(executor&& other) ASIO_NOEXCEPT + : impl_(other.impl_) + { + other.impl_ = 0; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Construct a polymorphic wrapper for the specified executor. + template + executor(Executor e); + + /// Allocator-aware constructor to create a polymorphic wrapper for the + /// specified executor. + template + executor(allocator_arg_t, const Allocator& a, Executor e); + + /// Destructor. + ~executor() + { + destroy(); + } + + /// Assignment operator. + executor& operator=(const executor& other) ASIO_NOEXCEPT + { + destroy(); + impl_ = other.clone(); + return *this; + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + // Move assignment operator. + executor& operator=(executor&& other) ASIO_NOEXCEPT + { + destroy(); + impl_ = other.impl_; + other.impl_ = 0; + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Assignment operator for nullptr_t. + executor& operator=(nullptr_t) ASIO_NOEXCEPT + { + destroy(); + impl_ = 0; + return *this; + } + + /// Assignment operator to create a polymorphic wrapper for the specified + /// executor. + template + executor& operator=(ASIO_MOVE_ARG(Executor) e) ASIO_NOEXCEPT + { + executor tmp(ASIO_MOVE_CAST(Executor)(e)); + destroy(); + impl_ = tmp.impl_; + tmp.impl_ = 0; + return *this; + } + + /// Obtain the underlying execution context. + execution_context& context() const ASIO_NOEXCEPT + { + return get_impl()->context(); + } + + /// Inform the executor that it has some outstanding work to do. + void on_work_started() const ASIO_NOEXCEPT + { + get_impl()->on_work_started(); + } + + /// Inform the executor that some work is no longer outstanding. + void on_work_finished() const ASIO_NOEXCEPT + { + get_impl()->on_work_finished(); + } + + /// Request the executor to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object is executed according to the rules of the + * target executor object. + * + * @param f The function object to be called. The executor will make a copy + * of the handler object as required. The function signature of the function + * object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the executor to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object is executed according to the rules of the + * target executor object. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the executor to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object is executed according to the rules of the + * target executor object. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + struct unspecified_bool_type_t {}; + typedef void (*unspecified_bool_type)(unspecified_bool_type_t); + static void unspecified_bool_true(unspecified_bool_type_t) {} + + /// Operator to test if the executor contains a valid target. + operator unspecified_bool_type() const ASIO_NOEXCEPT + { + return impl_ ? &executor::unspecified_bool_true : 0; + } + + /// Obtain type information for the target executor object. + /** + * @returns If @c *this has a target type of type @c T, typeid(T); + * otherwise, typeid(void). + */ +#if !defined(ASIO_NO_TYPEID) || defined(GENERATING_DOCUMENTATION) + const std::type_info& target_type() const ASIO_NOEXCEPT + { + return impl_ ? impl_->target_type() : typeid(void); + } +#else // !defined(ASIO_NO_TYPEID) || defined(GENERATING_DOCUMENTATION) + const void* target_type() const ASIO_NOEXCEPT + { + return impl_ ? impl_->target_type() : 0; + } +#endif // !defined(ASIO_NO_TYPEID) || defined(GENERATING_DOCUMENTATION) + + /// Obtain a pointer to the target executor object. + /** + * @returns If target_type() == typeid(T), a pointer to the stored + * executor target; otherwise, a null pointer. + */ + template + Executor* target() ASIO_NOEXCEPT; + + /// Obtain a pointer to the target executor object. + /** + * @returns If target_type() == typeid(T), a pointer to the stored + * executor target; otherwise, a null pointer. + */ + template + const Executor* target() const ASIO_NOEXCEPT; + + /// Compare two executors for equality. + friend bool operator==(const executor& a, + const executor& b) ASIO_NOEXCEPT + { + if (a.impl_ == b.impl_) + return true; + if (!a.impl_ || !b.impl_) + return false; + return a.impl_->equals(b.impl_); + } + + /// Compare two executors for inequality. + friend bool operator!=(const executor& a, + const executor& b) ASIO_NOEXCEPT + { + return !(a == b); + } + +private: +#if !defined(GENERATING_DOCUMENTATION) + class function; + template class impl; + +#if !defined(ASIO_NO_TYPEID) + typedef const std::type_info& type_id_result_type; +#else // !defined(ASIO_NO_TYPEID) + typedef const void* type_id_result_type; +#endif // !defined(ASIO_NO_TYPEID) + + template + static type_id_result_type type_id() + { +#if !defined(ASIO_NO_TYPEID) + return typeid(T); +#else // !defined(ASIO_NO_TYPEID) + static int unique_id; + return &unique_id; +#endif // !defined(ASIO_NO_TYPEID) + } + + // Base class for all polymorphic executor implementations. + class impl_base + { + public: + virtual impl_base* clone() const ASIO_NOEXCEPT = 0; + virtual void destroy() ASIO_NOEXCEPT = 0; + virtual execution_context& context() ASIO_NOEXCEPT = 0; + virtual void on_work_started() ASIO_NOEXCEPT = 0; + virtual void on_work_finished() ASIO_NOEXCEPT = 0; + virtual void dispatch(ASIO_MOVE_ARG(function)) = 0; + virtual void post(ASIO_MOVE_ARG(function)) = 0; + virtual void defer(ASIO_MOVE_ARG(function)) = 0; + virtual type_id_result_type target_type() const ASIO_NOEXCEPT = 0; + virtual void* target() ASIO_NOEXCEPT = 0; + virtual const void* target() const ASIO_NOEXCEPT = 0; + virtual bool equals(const impl_base* e) const ASIO_NOEXCEPT = 0; + + protected: + impl_base(bool fast_dispatch) : fast_dispatch_(fast_dispatch) {} + virtual ~impl_base() {} + + private: + friend class executor; + const bool fast_dispatch_; + }; + + // Helper function to check and return the implementation pointer. + impl_base* get_impl() const + { + if (!impl_) + { + bad_executor ex; + asio::detail::throw_exception(ex); + } + return impl_; + } + + // Helper function to clone another implementation. + impl_base* clone() const ASIO_NOEXCEPT + { + return impl_ ? impl_->clone() : 0; + } + + // Helper function to destroy an implementation. + void destroy() ASIO_NOEXCEPT + { + if (impl_) + impl_->destroy(); + } + + impl_base* impl_; +#endif // !defined(GENERATING_DOCUMENTATION) +}; + +} // namespace asio + +ASIO_USES_ALLOCATOR(asio::executor) + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/executor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/executor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/executor_work_guard.hpp b/subprojects/asio/include/asio/executor_work_guard.hpp new file mode 100644 index 000000000..1875f542b --- /dev/null +++ b/subprojects/asio/include/asio/executor_work_guard.hpp @@ -0,0 +1,170 @@ +// +// executor_work_guard.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTOR_WORK_GUARD_HPP +#define ASIO_EXECUTOR_WORK_GUARD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// An object of type @c executor_work_guard controls ownership of executor work +/// within a scope. +template +class executor_work_guard +{ +public: + /// The underlying executor type. + typedef Executor executor_type; + + /// Constructs a @c executor_work_guard object for the specified executor. + /** + * Stores a copy of @c e and calls on_work_started() on it. + */ + explicit executor_work_guard(const executor_type& e) ASIO_NOEXCEPT + : executor_(e), + owns_(true) + { + executor_.on_work_started(); + } + + /// Copy constructor. + executor_work_guard(const executor_work_guard& other) ASIO_NOEXCEPT + : executor_(other.executor_), + owns_(other.owns_) + { + if (owns_) + executor_.on_work_started(); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move constructor. + executor_work_guard(executor_work_guard&& other) + : executor_(ASIO_MOVE_CAST(Executor)(other.executor_)), + owns_(other.owns_) + { + other.owns_ = false; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor. + /** + * Unless the object has already been reset, or is in a moved-from state, + * calls on_work_finished() on the stored executor. + */ + ~executor_work_guard() + { + if (owns_) + executor_.on_work_finished(); + } + + /// Obtain the associated executor. + executor_type get_executor() const ASIO_NOEXCEPT + { + return executor_; + } + + /// Whether the executor_work_guard object owns some outstanding work. + bool owns_work() const ASIO_NOEXCEPT + { + return owns_; + } + + /// Indicate that the work is no longer outstanding. + /* + * Unless the object has already been reset, or is in a moved-from state, + * calls on_work_finished() on the stored executor. + */ + void reset() ASIO_NOEXCEPT + { + if (owns_) + { + executor_.on_work_finished(); + owns_ = false; + } + } + +private: + // Disallow assignment. + executor_work_guard& operator=(const executor_work_guard&); + + executor_type executor_; + bool owns_; +}; + +/// Create an @ref executor_work_guard object. +template +inline executor_work_guard make_work_guard(const Executor& ex, + typename enable_if::value>::type* = 0) +{ + return executor_work_guard(ex); +} + +/// Create an @ref executor_work_guard object. +template +inline executor_work_guard +make_work_guard(ExecutionContext& ctx, + typename enable_if< + is_convertible::value>::type* = 0) +{ + return executor_work_guard( + ctx.get_executor()); +} + +/// Create an @ref executor_work_guard object. +template +inline executor_work_guard::type> +make_work_guard(const T& t, + typename enable_if::value && + !is_convertible::value>::type* = 0) +{ + return executor_work_guard::type>( + associated_executor::get(t)); +} + +/// Create an @ref executor_work_guard object. +template +inline executor_work_guard::type> +make_work_guard(const T& t, const Executor& ex, + typename enable_if::value>::type* = 0) +{ + return executor_work_guard::type>( + associated_executor::get(t, ex)); +} + +/// Create an @ref executor_work_guard object. +template +inline executor_work_guard::type> +make_work_guard(const T& t, ExecutionContext& ctx, + typename enable_if::value && + !is_convertible::value && + is_convertible::value>::type* = 0) +{ + return executor_work_guard::type>( + associated_executor::get( + t, ctx.get_executor())); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTOR_WORK_GUARD_HPP diff --git a/subprojects/asio/include/asio/experimental.hpp b/subprojects/asio/include/asio/experimental.hpp new file mode 100644 index 000000000..5765c37d0 --- /dev/null +++ b/subprojects/asio/include/asio/experimental.hpp @@ -0,0 +1,22 @@ +// +// experimental.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_HPP +#define ASIO_EXPERIMENTAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/experimental/co_spawn.hpp" +#include "asio/experimental/detached.hpp" +#include "asio/experimental/redirect_error.hpp" + +#endif // ASIO_EXPERIMENTAL_HPP diff --git a/subprojects/asio/include/asio/experimental/co_spawn.hpp b/subprojects/asio/include/asio/experimental/co_spawn.hpp new file mode 100644 index 000000000..cbf5bc525 --- /dev/null +++ b/subprojects/asio/include/asio/experimental/co_spawn.hpp @@ -0,0 +1,226 @@ +// +// experimental/co_spawn.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_CO_SPAWN_HPP +#define ASIO_EXPERIMENTAL_CO_SPAWN_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/executor.hpp" +#include "asio/strand.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { +namespace detail { + +using std::experimental::coroutine_handle; + +template class awaiter; +template class awaitee_base; +template class awaitee; +template class await_handler_base; +template +auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token); + +} // namespace detail + +namespace this_coro { + +/// Awaitable type that returns a completion token for the current coroutine. +struct token_t {}; + +/// Awaitable object that returns a completion token for the current coroutine. +constexpr inline token_t token() { return {}; } + +/// Awaitable type that returns the executor of the current coroutine. +struct executor_t {}; + +/// Awaitable object that returns the executor of the current coroutine. +constexpr inline executor_t executor() { return {}; } + +} // namespace this_coro + +/// A completion token that represents the currently executing coroutine. +/** + * The await_token class is used to represent the currently executing + * coroutine. An await_token may be passed as a handler to an asynchronous + * operation. For example: + * + * @code awaitable my_coroutine() + * { + * await_token token = co_await this_coro::token(); + * ... + * std::size_t n = co_await my_socket.async_read_some(buffer, token); + * ... + * } @endcode + * + * The initiating function (async_read_some in the above example) suspends the + * current coroutine. The coroutine is resumed when the asynchronous operation + * completes, and the result of the operation is returned. + */ +template +class await_token +{ +public: + /// The associated executor type. + typedef Executor executor_type; + + /// Copy constructor. + await_token(const await_token& other) noexcept + : awaiter_(other.awaiter_) + { + } + + /// Move constructor. + await_token(await_token&& other) noexcept + : awaiter_(std::exchange(other.awaiter_, nullptr)) + { + } + + /// Get the associated executor. + executor_type get_executor() const noexcept + { + return awaiter_->get_executor(); + } + +private: + // No assignment allowed. + await_token& operator=(const await_token&) = delete; + + template friend class detail::awaitee_base; + template friend class detail::await_handler_base; + + // Private constructor used by awaitee_base. + explicit await_token(detail::awaiter* a) + : awaiter_(a) + { + } + + detail::awaiter* awaiter_; +}; + +/// The return type of a coroutine or asynchronous operation. +template > +class awaitable +{ +public: + /// The type of the awaited value. + typedef T value_type; + + /// The executor type that will be used for the coroutine. + typedef Executor executor_type; + + /// Move constructor. + awaitable(awaitable&& other) noexcept + : awaitee_(std::exchange(other.awaitee_, nullptr)) + { + } + + /// Destructor + ~awaitable() + { + if (awaitee_) + { + detail::coroutine_handle< + detail::awaitee>::from_promise( + *awaitee_).destroy(); + } + } + +#if !defined(GENERATING_DOCUMENTATION) + + // Support for co_await keyword. + bool await_ready() const noexcept + { + return awaitee_->ready(); + } + + // Support for co_await keyword. + void await_suspend(detail::coroutine_handle> h) + { + awaitee_->attach_caller(h); + } + + // Support for co_await keyword. + template + void await_suspend(detail::coroutine_handle> h) + { + awaitee_->attach_caller(h); + } + + // Support for co_await keyword. + T await_resume() + { + return awaitee_->get(); + } + +#endif // !defined(GENERATING_DOCUMENTATION) + +private: + template friend class detail::awaitee; + template friend class detail::await_handler_base; + + // Not copy constructible or copy assignable. + awaitable(const awaitable&) = delete; + awaitable& operator=(const awaitable&) = delete; + + // Construct the awaitable from a coroutine's promise object. + explicit awaitable(detail::awaitee* a) : awaitee_(a) {} + + detail::awaitee* awaitee_; +}; + +/// Spawn a new thread of execution. +template ::value>::type> +inline auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token) +{ + return detail::co_spawn(ex, std::forward(f), + std::forward(token)); +} + +/// Spawn a new thread of execution. +template ::value>::type> +inline auto co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token) +{ + return detail::co_spawn(ctx.get_executor(), std::forward(f), + std::forward(token)); +} + +/// Spawn a new thread of execution. +template +inline auto co_spawn(const await_token& parent, + F&& f, CompletionToken&& token) +{ + return detail::co_spawn(parent.get_executor(), std::forward(f), + std::forward(token)); +} + +} // namespace experimental +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/experimental/impl/co_spawn.hpp" + +#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_EXPERIMENTAL_CO_SPAWN_HPP diff --git a/subprojects/asio/include/asio/experimental/detached.hpp b/subprojects/asio/include/asio/experimental/detached.hpp new file mode 100644 index 000000000..d484f18fb --- /dev/null +++ b/subprojects/asio/include/asio/experimental/detached.hpp @@ -0,0 +1,65 @@ +// +// experimental/detached.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_DETACHED_HPP +#define ASIO_EXPERIMENTAL_DETACHED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { + +/// Class used to specify that an asynchronous operation is detached. +/** + + * The detached_t class is used to indicate that an asynchronous operation is + * detached. That is, there is no completion handler waiting for the + * operation's result. A detached_t object may be passed as a handler to an + * asynchronous operation, typically using the special value + * @c asio::experimental::detached. For example: + + * @code my_socket.async_send(my_buffer, asio::experimental::detached); + * @endcode + */ +class detached_t +{ +public: + /// Constructor. + ASIO_CONSTEXPR detached_t() + { + } +}; + +/// A special value, similar to std::nothrow. +/** + * See the documentation for asio::experimental::detached_t for a usage + * example. + */ +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr detached_t detached; +#elif defined(ASIO_MSVC) +__declspec(selectany) detached_t detached; +#endif + +} // namespace experimental +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/experimental/impl/detached.hpp" + +#endif // ASIO_EXPERIMENTAL_DETACHED_HPP diff --git a/subprojects/asio/include/asio/experimental/impl/co_spawn.hpp b/subprojects/asio/include/asio/experimental/impl/co_spawn.hpp new file mode 100644 index 000000000..8263eff96 --- /dev/null +++ b/subprojects/asio/include/asio/experimental/impl/co_spawn.hpp @@ -0,0 +1,876 @@ +// +// experimental/impl/co_spawn.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP +#define ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/async_result.hpp" +#include "asio/detail/thread_context.hpp" +#include "asio/detail/thread_info_base.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/dispatch.hpp" +#include "asio/post.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { +namespace detail { + +// Promise object for coroutine at top of thread-of-execution "stack". +template +class awaiter +{ +public: + struct deleter + { + void operator()(awaiter* a) + { + if (a) + a->release(); + } + }; + + typedef std::unique_ptr ptr; + + typedef Executor executor_type; + + ~awaiter() + { + if (has_executor_) + static_cast(static_cast(executor_))->~Executor(); + } + + void set_executor(const Executor& ex) + { + new (&executor_) Executor(ex); + has_executor_ = true; + } + + executor_type get_executor() const noexcept + { + return *static_cast(static_cast(executor_)); + } + + awaiter* get_return_object() + { + return this; + } + + auto initial_suspend() + { + return std::experimental::suspend_always(); + } + + auto final_suspend() + { + return std::experimental::suspend_always(); + } + + void return_void() + { + } + + awaiter* add_ref() + { + ++ref_count_; + return this; + } + + void release() + { + if (--ref_count_ == 0) + coroutine_handle::from_promise(*this).destroy(); + } + + void unhandled_exception() + { + pending_exception_ = std::current_exception(); + } + + void rethrow_unhandled_exception() + { + if (pending_exception_) + { + std::exception_ptr ex = std::exchange(pending_exception_, nullptr); + std::rethrow_exception(ex); + } + } + +private: + std::size_t ref_count_ = 0; + std::exception_ptr pending_exception_ = nullptr; + alignas(Executor) unsigned char executor_[sizeof(Executor)]; + bool has_executor_ = false; +}; + +// Base promise for coroutines further down the thread-of-execution "stack". +template +class awaitee_base +{ +public: +#if !defined(ASIO_DISABLE_AWAITEE_RECYCLING) + void* operator new(std::size_t size) + { + return asio::detail::thread_info_base::allocate( + asio::detail::thread_info_base::awaitee_tag(), + asio::detail::thread_context::thread_call_stack::top(), + size); + } + + void operator delete(void* pointer, std::size_t size) + { + asio::detail::thread_info_base::deallocate( + asio::detail::thread_info_base::awaitee_tag(), + asio::detail::thread_context::thread_call_stack::top(), + pointer, size); + } +#endif // !defined(ASIO_DISABLE_AWAITEE_RECYCLING) + + auto initial_suspend() + { + return std::experimental::suspend_never(); + } + + struct final_suspender + { + awaitee_base* this_; + + bool await_ready() const noexcept + { + return false; + } + + void await_suspend(coroutine_handle) + { + this_->wake_caller(); + } + + void await_resume() const noexcept + { + } + }; + + auto final_suspend() + { + return final_suspender{this}; + } + + void set_except(std::exception_ptr e) + { + pending_exception_ = e; + } + + void unhandled_exception() + { + set_except(std::current_exception()); + } + + void rethrow_exception() + { + if (pending_exception_) + { + std::exception_ptr ex = std::exchange(pending_exception_, nullptr); + std::rethrow_exception(ex); + } + } + + awaiter* top() + { + return awaiter_; + } + + coroutine_handle caller() + { + return caller_; + } + + bool ready() const + { + return ready_; + } + + void wake_caller() + { + if (caller_) + caller_.resume(); + else + ready_ = true; + } + + class awaitable_executor + { + public: + explicit awaitable_executor(awaitee_base* a) + : this_(a) + { + } + + bool await_ready() const noexcept + { + return this_->awaiter_ != nullptr; + } + + template + void await_suspend(coroutine_handle> h) noexcept + { + this_->resume_on_attach_ = h; + } + + Executor await_resume() + { + return this_->awaiter_->get_executor(); + } + + private: + awaitee_base* this_; + }; + + awaitable_executor await_transform(this_coro::executor_t) noexcept + { + return awaitable_executor(this); + } + + class awaitable_token + { + public: + explicit awaitable_token(awaitee_base* a) + : this_(a) + { + } + + bool await_ready() const noexcept + { + return this_->awaiter_ != nullptr; + } + + template + void await_suspend(coroutine_handle> h) noexcept + { + this_->resume_on_attach_ = h; + } + + await_token await_resume() + { + return await_token(this_->awaiter_); + } + + private: + awaitee_base* this_; + }; + + awaitable_token await_transform(this_coro::token_t) noexcept + { + return awaitable_token(this); + } + + template + awaitable await_transform(awaitable& t) const + { + return std::move(t); + } + + template + awaitable await_transform(awaitable&& t) const + { + return std::move(t); + } + + std::experimental::suspend_always await_transform( + std::experimental::suspend_always) const + { + return std::experimental::suspend_always(); + } + + void attach_caller(coroutine_handle> h) + { + this->caller_ = h; + this->attach_callees(&h.promise()); + } + + template + void attach_caller(coroutine_handle> h) + { + this->caller_ = h; + if (h.promise().awaiter_) + this->attach_callees(h.promise().awaiter_); + else + h.promise().unattached_callee_ = this; + } + + void attach_callees(awaiter* a) + { + for (awaitee_base* curr = this; curr != nullptr; + curr = std::exchange(curr->unattached_callee_, nullptr)) + { + curr->awaiter_ = a; + if (curr->resume_on_attach_) + return std::exchange(curr->resume_on_attach_, nullptr).resume(); + } + } + +protected: + awaiter* awaiter_ = nullptr; + coroutine_handle caller_ = nullptr; + awaitee_base* unattached_callee_ = nullptr; + std::exception_ptr pending_exception_ = nullptr; + coroutine_handle resume_on_attach_ = nullptr; + bool ready_ = false; +}; + +// Promise object for coroutines further down the thread-of-execution "stack". +template +class awaitee + : public awaitee_base +{ +public: + awaitee() + { + } + + awaitee(awaitee&& other) noexcept + : awaitee_base(std::move(other)) + { + } + + ~awaitee() + { + if (has_result_) + static_cast(static_cast(result_))->~T(); + } + + awaitable get_return_object() + { + return awaitable(this); + }; + + template + void return_value(U&& u) + { + new (&result_) T(std::forward(u)); + has_result_ = true; + } + + T get() + { + this->caller_ = nullptr; + this->rethrow_exception(); + return std::move(*static_cast(static_cast(result_))); + } + +private: + alignas(T) unsigned char result_[sizeof(T)]; + bool has_result_ = false; +}; + +// Promise object for coroutines further down the thread-of-execution "stack". +template +class awaitee + : public awaitee_base +{ +public: + awaitable get_return_object() + { + return awaitable(this); + }; + + void return_void() + { + } + + void get() + { + this->caller_ = nullptr; + this->rethrow_exception(); + } +}; + +template +class awaiter_task +{ +public: + typedef Executor executor_type; + + awaiter_task(awaiter* a) + : awaiter_(a->add_ref()) + { + } + + awaiter_task(awaiter_task&& other) noexcept + : awaiter_(std::exchange(other.awaiter_, nullptr)) + { + } + + ~awaiter_task() + { + if (awaiter_) + { + // Coroutine "stack unwinding" must be performed through the executor. + executor_type ex(awaiter_->get_executor()); + (post)(ex, + [a = std::move(awaiter_)]() mutable + { + typename awaiter::ptr(std::move(a)); + }); + } + } + + executor_type get_executor() const noexcept + { + return awaiter_->get_executor(); + } + +protected: + typename awaiter::ptr awaiter_; +}; + +template +class co_spawn_handler : public awaiter_task +{ +public: + using awaiter_task::awaiter_task; + + void operator()() + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + coroutine_handle>::from_promise(*ptr.get()).resume(); + } +}; + +template +class await_handler_base : public awaiter_task +{ +public: + typedef awaitable awaitable_type; + + await_handler_base(await_token token) + : awaiter_task(token.awaiter_), + awaitee_(nullptr) + { + } + + await_handler_base(await_handler_base&& other) noexcept + : awaiter_task(std::move(other)), + awaitee_(std::exchange(other.awaitee_, nullptr)) + { + } + + void attach_awaitee(const awaitable& a) + { + awaitee_ = a.awaitee_; + } + +protected: + awaitee* awaitee_; +}; + +template class await_handler; + +template +class await_handler + : public await_handler_base +{ +public: + using await_handler_base::await_handler_base; + + void operator()() + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + this->awaitee_->return_void(); + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base +{ +public: + typedef void return_type; + + using await_handler_base::await_handler_base; + + void operator()(const asio::error_code& ec) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + if (ec) + { + this->awaitee_->set_except( + std::make_exception_ptr(asio::system_error(ec))); + } + else + this->awaitee_->return_void(); + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base +{ +public: + using await_handler_base::await_handler_base; + + void operator()(std::exception_ptr ex) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + if (ex) + this->awaitee_->set_except(ex); + else + this->awaitee_->return_void(); + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base +{ +public: + using await_handler_base::await_handler_base; + + template + void operator()(Arg&& arg) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + this->awaitee_->return_value(std::forward(arg)); + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base +{ +public: + using await_handler_base::await_handler_base; + + template + void operator()(const asio::error_code& ec, Arg&& arg) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + if (ec) + { + this->awaitee_->set_except( + std::make_exception_ptr(asio::system_error(ec))); + } + else + this->awaitee_->return_value(std::forward(arg)); + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base +{ +public: + using await_handler_base::await_handler_base; + + template + void operator()(std::exception_ptr ex, Arg&& arg) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + if (ex) + this->awaitee_->set_except(ex); + else + this->awaitee_->return_value(std::forward(arg)); + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base> +{ +public: + using await_handler_base>::await_handler_base; + + template + void operator()(Args&&... args) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + this->awaitee_->return_value( + std::forward_as_tuple(std::forward(args)...)); + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base> +{ +public: + using await_handler_base>::await_handler_base; + + template + void operator()(const asio::error_code& ec, Args&&... args) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + if (ec) + { + this->awaitee_->set_except( + std::make_exception_ptr(asio::system_error(ec))); + } + else + { + this->awaitee_->return_value( + std::forward_as_tuple(std::forward(args)...)); + } + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +class await_handler + : public await_handler_base> +{ +public: + using await_handler_base>::await_handler_base; + + template + void operator()(std::exception_ptr ex, Args&&... args) + { + typename awaiter::ptr ptr(std::move(this->awaiter_)); + if (ex) + this->awaitee_->set_except(ex); + else + { + this->awaitee_->return_value( + std::forward_as_tuple(std::forward(args)...)); + } + this->awaitee_->wake_caller(); + ptr->rethrow_unhandled_exception(); + } +}; + +template +struct awaitable_signature; + +template +struct awaitable_signature> +{ + typedef void type(std::exception_ptr, T); +}; + +template +struct awaitable_signature> +{ + typedef void type(std::exception_ptr); +}; + +template +awaiter* co_spawn_entry_point(awaitable*, + executor_work_guard work_guard, F f, Handler handler) +{ + bool done = false; + + try + { + T t = co_await f(); + + done = true; + + (dispatch)(work_guard.get_executor(), + [handler = std::move(handler), t = std::move(t)]() mutable + { + handler(std::exception_ptr(), std::move(t)); + }); + } + catch (...) + { + if (done) + throw; + + (dispatch)(work_guard.get_executor(), + [handler = std::move(handler), e = std::current_exception()]() mutable + { + handler(e, T()); + }); + } +} + +template +awaiter* co_spawn_entry_point(awaitable*, + executor_work_guard work_guard, F f, Handler handler) +{ + std::exception_ptr e = nullptr; + + try + { + co_await f(); + } + catch (...) + { + e = std::current_exception(); + } + + (dispatch)(work_guard.get_executor(), + [handler = std::move(handler), e]() mutable + { + handler(e); + }); +} + +template +auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token) +{ + typedef typename result_of::type awaitable_type; + typedef typename awaitable_type::executor_type executor_type; + typedef typename awaitable_signature::type signature_type; + + async_completion completion(token); + + executor_type ex2(ex); + auto work_guard = make_work_guard(completion.completion_handler, ex2); + + auto* a = (co_spawn_entry_point)( + static_cast(nullptr), std::move(work_guard), + std::forward(f), std::move(completion.completion_handler)); + + a->set_executor(ex2); + (post)(co_spawn_handler(a)); + + return completion.result.get(); +} + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4033) +#endif // defined(_MSC_VER) + +#if defined(_MSC_VER) +template T dummy_return() +{ + return std::move(*static_cast(nullptr)); +} + +template <> +inline void dummy_return() +{ +} +#endif // defined(_MSC_VER) + +template +inline Awaitable make_dummy_awaitable() +{ + for (;;) co_await std::experimental::suspend_always(); +#if defined(_MSC_VER) + co_return dummy_return(); +#endif // defined(_MSC_VER) +} + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif // defined(_MSC_VER) + +} // namespace detail +} // namespace experimental + +template +class async_result, R(Args...)> +{ +public: + typedef experimental::detail::await_handler< + Executor, typename decay::type...> completion_handler_type; + + typedef typename experimental::detail::await_handler< + Executor, Args...>::awaitable_type return_type; + + async_result(completion_handler_type& h) + : awaitable_(experimental::detail::make_dummy_awaitable()) + { + h.attach_awaitee(awaitable_); + } + + return_type get() + { + return std::move(awaitable_); + } + +private: + return_type awaitable_; +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template +struct handler_type, R(Args...)> +{ + typedef experimental::detail::await_handler< + Executor, typename decay::type...> type; +}; + +template +class async_result> +{ +public: + typedef typename experimental::detail::await_handler< + Executor, Args...>::awaitable_type type; + + async_result(experimental::detail::await_handler& h) + : awaitable_(experimental::detail::make_dummy_awaitable()) + { + h.attach_awaitee(awaitable_); + } + + type get() + { + return std::move(awaitable_); + } + +private: + type awaitable_; +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +} // namespace asio + +namespace std { namespace experimental { + +template +struct coroutine_traits< + asio::experimental::detail::awaiter*, Args...> +{ + typedef asio::experimental::detail::awaiter promise_type; +}; + +template +struct coroutine_traits< + asio::experimental::awaitable, Args...> +{ + typedef asio::experimental::detail::awaitee promise_type; +}; + +}} // namespace std::experimental + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP diff --git a/subprojects/asio/include/asio/experimental/impl/detached.hpp b/subprojects/asio/include/asio/experimental/impl/detached.hpp new file mode 100644 index 000000000..6ce88872e --- /dev/null +++ b/subprojects/asio/include/asio/experimental/impl/detached.hpp @@ -0,0 +1,91 @@ +// +// experimental/impl/detached.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP +#define ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/variadic_templates.hpp" +#include "asio/handler_type.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { +namespace detail { + + // Class to adapt a detached_t as a completion handler. + class detached_handler + { + public: + detached_handler(detached_t) + { + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()(Args...) + { + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + void operator()() + { + } + +#define ASIO_PRIVATE_DETACHED_DEF(n) \ + template \ + void operator()(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ + { \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_DETACHED_DEF) +#undef ASIO_PRIVATE_DETACHED_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + }; + +} // namespace detail +} // namespace experimental + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct async_result +{ + typedef asio::experimental::detail::detached_handler + completion_handler_type; + + typedef void return_type; + + explicit async_result(completion_handler_type&) + { + } + + void get() + { + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP diff --git a/subprojects/asio/include/asio/experimental/impl/redirect_error.hpp b/subprojects/asio/include/asio/experimental/impl/redirect_error.hpp new file mode 100644 index 000000000..d3b97facc --- /dev/null +++ b/subprojects/asio/include/asio/experimental/impl/redirect_error.hpp @@ -0,0 +1,294 @@ +// +// experimental/impl/redirect_error.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP +#define ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_executor.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" +#include "asio/handler_type.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { +namespace detail { + +// Class to adapt a redirect_error_t as a completion handler. +template +class redirect_error_handler +{ +public: + template + redirect_error_handler(redirect_error_t e) + : ec_(e.ec_), + handler_(ASIO_MOVE_CAST(CompletionToken)(e.token_)) + { + } + + void operator()() + { + handler_(); + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + typename enable_if< + !is_same::type, asio::error_code>::value + >::type + operator()(ASIO_MOVE_ARG(Arg) arg, ASIO_MOVE_ARG(Args)... args) + { + handler_(ASIO_MOVE_CAST(Arg)(arg), + ASIO_MOVE_CAST(Args)(args)...); + } + + template + void operator()(const asio::error_code& ec, + ASIO_MOVE_ARG(Args)... args) + { + ec_ = ec; + handler_(ASIO_MOVE_CAST(Args)(args)...); + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + typename enable_if< + !is_same::type, asio::error_code>::value + >::type + operator()(ASIO_MOVE_ARG(Arg) arg) + { + handler_(ASIO_MOVE_CAST(Arg)(arg)); + } + + void operator()(const asio::error_code& ec) + { + ec_ = ec; + handler_(); + } + +#define ASIO_PRIVATE_REDIRECT_ERROR_DEF(n) \ + template \ + typename enable_if< \ + !is_same::type, asio::error_code>::value \ + >::type \ + operator()(ASIO_MOVE_ARG(Arg) arg, ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + handler_(ASIO_MOVE_CAST(Arg)(arg), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + \ + template \ + void operator()(const asio::error_code& ec, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + ec_ = ec; \ + handler_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_REDIRECT_ERROR_DEF) +#undef ASIO_PRIVATE_REDIRECT_ERROR_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +//private: + asio::error_code& ec_; + Handler handler_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + redirect_error_handler* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + redirect_error_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + redirect_error_handler* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + redirect_error_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + redirect_error_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +struct redirect_error_signature +{ + typedef Signature type; +}; + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct redirect_error_signature +{ + typedef R type(Args...); +}; + +template +struct redirect_error_signature +{ + typedef R type(Args...); +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct redirect_error_signature +{ + typedef R type(); +}; + +template +struct redirect_error_signature +{ + typedef R type(); +}; + +#define ASIO_PRIVATE_REDIRECT_ERROR_DEF(n) \ + template \ + struct redirect_error_signature< \ + R(asio::error_code, ASIO_VARIADIC_TARGS(n))> \ + { \ + typedef R type(ASIO_VARIADIC_TARGS(n)); \ + }; \ + \ + template \ + struct redirect_error_signature< \ + R(const asio::error_code&, ASIO_VARIADIC_TARGS(n))> \ + { \ + typedef R type(ASIO_VARIADIC_TARGS(n)); \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_REDIRECT_ERROR_DEF) +#undef ASIO_PRIVATE_REDIRECT_ERROR_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +} // namespace detail +} // namespace experimental + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct async_result, Signature> + : async_result::type> +{ + typedef experimental::detail::redirect_error_handler< + typename async_result::type> + ::completion_handler_type> completion_handler_type; + + explicit async_result(completion_handler_type& h) + : async_result::type>(h.handler_) + { + } +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template +struct handler_type, Signature> +{ + typedef experimental::detail::redirect_error_handler< + typename async_result::type> + ::completion_handler_type> type; +}; + +template +struct async_result > + : async_result +{ + explicit async_result( + experimental::detail::redirect_error_handler& h) + : async_result(h.handler_) + { + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +struct associated_executor< + experimental::detail::redirect_error_handler, Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const experimental::detail::redirect_error_handler& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +template +struct associated_allocator< + experimental::detail::redirect_error_handler, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const experimental::detail::redirect_error_handler& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP diff --git a/subprojects/asio/include/asio/experimental/redirect_error.hpp b/subprojects/asio/include/asio/experimental/redirect_error.hpp new file mode 100644 index 000000000..30e81cf5a --- /dev/null +++ b/subprojects/asio/include/asio/experimental/redirect_error.hpp @@ -0,0 +1,67 @@ +// +// experimental/redirect_error.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP +#define ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { + +/// Completion token type used to specify that an error produced by an +/// asynchronous operation is captured to an error_code variable. +/** + * The redirect_error_t class is used to indicate that any error_code produced + * by an asynchronous operation is captured to a specified variable. + */ +template +class redirect_error_t +{ +public: + /// Constructor. + template + redirect_error_t(ASIO_MOVE_ARG(T) completion_token, + asio::error_code& ec) + : token_(ASIO_MOVE_CAST(T)(completion_token)), + ec_(ec) + { + } + +//private: + CompletionToken token_; + asio::error_code& ec_; +}; + +/// Create a completion token to capture error_code values to a variable. +template +inline redirect_error_t::type> redirect_error( + CompletionToken&& completion_token, asio::error_code& ec) +{ + return redirect_error_t::type>( + ASIO_MOVE_CAST(CompletionToken)(completion_token), ec); +} + +} // namespace experimental +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/experimental/impl/redirect_error.hpp" + +#endif // ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP diff --git a/subprojects/asio/include/asio/generic/basic_endpoint.hpp b/subprojects/asio/include/asio/generic/basic_endpoint.hpp new file mode 100644 index 000000000..73aa151d6 --- /dev/null +++ b/subprojects/asio/include/asio/generic/basic_endpoint.hpp @@ -0,0 +1,193 @@ +// +// generic/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_GENERIC_BASIC_ENDPOINT_HPP +#define ASIO_GENERIC_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/generic/detail/endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace generic { + +/// Describes an endpoint for any socket type. +/** + * The asio::generic::basic_endpoint class template describes an endpoint + * that may be associated with any socket type. + * + * @note The socket types sockaddr type must be able to fit into a + * @c sockaddr_storage structure. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef Protocol protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef asio::detail::socket_addr_type data_type; +#endif + + /// Default constructor. + basic_endpoint() + { + } + + /// Construct an endpoint from the specified socket address. + basic_endpoint(const void* socket_address, + std::size_t socket_address_size, int socket_protocol = 0) + : impl_(socket_address, socket_address_size, socket_protocol) + { + } + + /// Construct an endpoint from the specific endpoint type. + template + basic_endpoint(const Endpoint& endpoint) + : impl_(endpoint.data(), endpoint.size(), endpoint.protocol().protocol()) + { + } + + /// Copy constructor. + basic_endpoint(const basic_endpoint& other) + : impl_(other.impl_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + basic_endpoint(basic_endpoint&& other) + : impl_(other.impl_) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from another endpoint. + basic_endpoint& operator=(const basic_endpoint& other) + { + impl_ = other.impl_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move-assign from another endpoint. + basic_endpoint& operator=(basic_endpoint&& other) + { + impl_ = other.impl_; + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + return protocol_type(impl_.family(), impl_.protocol()); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return impl_.data(); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return impl_.data(); + } + + /// Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return impl_.size(); + } + + /// Set the underlying size of the endpoint in the native type. + void resize(std::size_t new_size) + { + impl_.resize(new_size); + } + + /// Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return impl_.capacity(); + } + + /// Compare two endpoints for equality. + friend bool operator==(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.impl_ == e2.impl_; + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1.impl_ == e2.impl_); + } + + /// Compare endpoints for ordering. + friend bool operator<(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.impl_ < e2.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator>(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e2.impl_ < e1.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator<=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e2 < e1); + } + + /// Compare endpoints for ordering. + friend bool operator>=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1 < e2); + } + +private: + // The underlying generic endpoint. + asio::generic::detail::endpoint impl_; +}; + +} // namespace generic +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_GENERIC_BASIC_ENDPOINT_HPP diff --git a/subprojects/asio/include/asio/generic/datagram_protocol.hpp b/subprojects/asio/include/asio/generic/datagram_protocol.hpp new file mode 100644 index 000000000..8678ad85b --- /dev/null +++ b/subprojects/asio/include/asio/generic/datagram_protocol.hpp @@ -0,0 +1,123 @@ +// +// generic/datagram_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_GENERIC_DATAGRAM_PROTOCOL_HPP +#define ASIO_GENERIC_DATAGRAM_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/basic_datagram_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/generic/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace generic { + +/// Encapsulates the flags needed for a generic datagram-oriented socket. +/** + * The asio::generic::datagram_protocol class contains flags necessary + * for datagram-oriented sockets of any address family and protocol. + * + * @par Examples + * Constructing using a native address family and socket protocol: + * @code datagram_protocol p(AF_INET, IPPROTO_UDP); @endcode + * Constructing from a specific protocol type: + * @code datagram_protocol p(asio::ip::udp::v4()); @endcode + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class datagram_protocol +{ +public: + /// Construct a protocol object for a specific address family and protocol. + datagram_protocol(int address_family, int socket_protocol) + : family_(address_family), + protocol_(socket_protocol) + { + } + + /// Construct a generic protocol object from a specific protocol. + /** + * @throws @c bad_cast Thrown if the source protocol is not datagram-oriented. + */ + template + datagram_protocol(const Protocol& source_protocol) + : family_(source_protocol.family()), + protocol_(source_protocol.protocol()) + { + if (source_protocol.type() != type()) + { + std::bad_cast ex; + asio::detail::throw_exception(ex); + } + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return ASIO_OS_DEF(SOCK_DGRAM); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return protocol_; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// Compare two protocols for equality. + friend bool operator==(const datagram_protocol& p1, + const datagram_protocol& p2) + { + return p1.family_ == p2.family_ && p1.protocol_ == p2.protocol_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const datagram_protocol& p1, + const datagram_protocol& p2) + { + return !(p1 == p2); + } + + /// The type of an endpoint. + typedef basic_endpoint endpoint; + + /// The generic socket type. + typedef basic_datagram_socket socket; + +private: + int family_; + int protocol_; +}; + +} // namespace generic +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_GENERIC_DATAGRAM_PROTOCOL_HPP diff --git a/subprojects/asio/include/asio/generic/detail/endpoint.hpp b/subprojects/asio/include/asio/generic/detail/endpoint.hpp new file mode 100644 index 000000000..190beb14c --- /dev/null +++ b/subprojects/asio/include/asio/generic/detail/endpoint.hpp @@ -0,0 +1,133 @@ +// +// generic/detail/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_GENERIC_DETAIL_ENDPOINT_HPP +#define ASIO_GENERIC_DETAIL_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace generic { +namespace detail { + +// Helper class for implementing a generic socket endpoint. +class endpoint +{ +public: + // Default constructor. + ASIO_DECL endpoint(); + + // Construct an endpoint from the specified raw bytes. + ASIO_DECL endpoint(const void* sock_addr, + std::size_t sock_addr_size, int sock_protocol); + + // Copy constructor. + endpoint(const endpoint& other) + : data_(other.data_), + size_(other.size_), + protocol_(other.protocol_) + { + } + + // Assign from another endpoint. + endpoint& operator=(const endpoint& other) + { + data_ = other.data_; + size_ = other.size_; + protocol_ = other.protocol_; + return *this; + } + + // Get the address family associated with the endpoint. + int family() const + { + return data_.base.sa_family; + } + + // Get the socket protocol associated with the endpoint. + int protocol() const + { + return protocol_; + } + + // Get the underlying endpoint in the native type. + asio::detail::socket_addr_type* data() + { + return &data_.base; + } + + // Get the underlying endpoint in the native type. + const asio::detail::socket_addr_type* data() const + { + return &data_.base; + } + + // Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return size_; + } + + // Set the underlying size of the endpoint in the native type. + ASIO_DECL void resize(std::size_t size); + + // Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return sizeof(asio::detail::sockaddr_storage_type); + } + + // Compare two endpoints for equality. + ASIO_DECL friend bool operator==( + const endpoint& e1, const endpoint& e2); + + // Compare endpoints for ordering. + ASIO_DECL friend bool operator<( + const endpoint& e1, const endpoint& e2); + +private: + // The underlying socket address. + union data_union + { + asio::detail::socket_addr_type base; + asio::detail::sockaddr_storage_type generic; + } data_; + + // The length of the socket address stored in the endpoint. + std::size_t size_; + + // The socket protocol associated with the endpoint. + int protocol_; + + // Initialise with a specified memory. + ASIO_DECL void init(const void* sock_addr, + std::size_t sock_addr_size, int sock_protocol); +}; + +} // namespace detail +} // namespace generic +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/generic/detail/impl/endpoint.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_GENERIC_DETAIL_ENDPOINT_HPP diff --git a/subprojects/asio/include/asio/generic/detail/impl/endpoint.ipp b/subprojects/asio/include/asio/generic/detail/impl/endpoint.ipp new file mode 100644 index 000000000..227ffe1e3 --- /dev/null +++ b/subprojects/asio/include/asio/generic/detail/impl/endpoint.ipp @@ -0,0 +1,110 @@ +// +// generic/detail/impl/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_GENERIC_DETAIL_IMPL_ENDPOINT_IPP +#define ASIO_GENERIC_DETAIL_IMPL_ENDPOINT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/error.hpp" +#include "asio/generic/detail/endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace generic { +namespace detail { + +endpoint::endpoint() +{ + init(0, 0, 0); +} + +endpoint::endpoint(const void* sock_addr, + std::size_t sock_addr_size, int sock_protocol) +{ + init(sock_addr, sock_addr_size, sock_protocol); +} + +void endpoint::resize(std::size_t new_size) +{ + if (new_size > sizeof(asio::detail::sockaddr_storage_type)) + { + asio::error_code ec(asio::error::invalid_argument); + asio::detail::throw_error(ec); + } + else + { + size_ = new_size; + protocol_ = 0; + } +} + +bool operator==(const endpoint& e1, const endpoint& e2) +{ + using namespace std; // For memcmp. + return e1.size() == e2.size() && memcmp(e1.data(), e2.data(), e1.size()) == 0; +} + +bool operator<(const endpoint& e1, const endpoint& e2) +{ + if (e1.protocol() < e2.protocol()) + return true; + + if (e1.protocol() > e2.protocol()) + return false; + + using namespace std; // For memcmp. + std::size_t compare_size = e1.size() < e2.size() ? e1.size() : e2.size(); + int compare_result = memcmp(e1.data(), e2.data(), compare_size); + + if (compare_result < 0) + return true; + + if (compare_result > 0) + return false; + + return e1.size() < e2.size(); +} + +void endpoint::init(const void* sock_addr, + std::size_t sock_addr_size, int sock_protocol) +{ + if (sock_addr_size > sizeof(asio::detail::sockaddr_storage_type)) + { + asio::error_code ec(asio::error::invalid_argument); + asio::detail::throw_error(ec); + } + + using namespace std; // For memset and memcpy. + memset(&data_.generic, 0, sizeof(asio::detail::sockaddr_storage_type)); + if (sock_addr_size > 0) + memcpy(&data_.generic, sock_addr, sock_addr_size); + + size_ = sock_addr_size; + protocol_ = sock_protocol; +} + +} // namespace detail +} // namespace generic +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_GENERIC_DETAIL_IMPL_ENDPOINT_IPP diff --git a/subprojects/asio/include/asio/generic/raw_protocol.hpp b/subprojects/asio/include/asio/generic/raw_protocol.hpp new file mode 100644 index 000000000..b83dca6e3 --- /dev/null +++ b/subprojects/asio/include/asio/generic/raw_protocol.hpp @@ -0,0 +1,121 @@ +// +// generic/raw_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_GENERIC_RAW_PROTOCOL_HPP +#define ASIO_GENERIC_RAW_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/basic_raw_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/generic/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace generic { + +/// Encapsulates the flags needed for a generic raw socket. +/** + * The asio::generic::raw_protocol class contains flags necessary for + * raw sockets of any address family and protocol. + * + * @par Examples + * Constructing using a native address family and socket protocol: + * @code raw_protocol p(AF_INET, IPPROTO_ICMP); @endcode + * Constructing from a specific protocol type: + * @code raw_protocol p(asio::ip::icmp::v4()); @endcode + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class raw_protocol +{ +public: + /// Construct a protocol object for a specific address family and protocol. + raw_protocol(int address_family, int socket_protocol) + : family_(address_family), + protocol_(socket_protocol) + { + } + + /// Construct a generic protocol object from a specific protocol. + /** + * @throws @c bad_cast Thrown if the source protocol is not raw-oriented. + */ + template + raw_protocol(const Protocol& source_protocol) + : family_(source_protocol.family()), + protocol_(source_protocol.protocol()) + { + if (source_protocol.type() != type()) + { + std::bad_cast ex; + asio::detail::throw_exception(ex); + } + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return ASIO_OS_DEF(SOCK_RAW); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return protocol_; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// Compare two protocols for equality. + friend bool operator==(const raw_protocol& p1, const raw_protocol& p2) + { + return p1.family_ == p2.family_ && p1.protocol_ == p2.protocol_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const raw_protocol& p1, const raw_protocol& p2) + { + return !(p1 == p2); + } + + /// The type of an endpoint. + typedef basic_endpoint endpoint; + + /// The generic socket type. + typedef basic_raw_socket socket; + +private: + int family_; + int protocol_; +}; + +} // namespace generic +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_GENERIC_RAW_PROTOCOL_HPP diff --git a/subprojects/asio/include/asio/generic/seq_packet_protocol.hpp b/subprojects/asio/include/asio/generic/seq_packet_protocol.hpp new file mode 100644 index 000000000..f92a4c8f8 --- /dev/null +++ b/subprojects/asio/include/asio/generic/seq_packet_protocol.hpp @@ -0,0 +1,122 @@ +// +// generic/seq_packet_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_GENERIC_SEQ_PACKET_PROTOCOL_HPP +#define ASIO_GENERIC_SEQ_PACKET_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/basic_seq_packet_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/generic/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace generic { + +/// Encapsulates the flags needed for a generic sequenced packet socket. +/** + * The asio::generic::seq_packet_protocol class contains flags necessary + * for seq_packet-oriented sockets of any address family and protocol. + * + * @par Examples + * Constructing using a native address family and socket protocol: + * @code seq_packet_protocol p(AF_INET, IPPROTO_SCTP); @endcode + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class seq_packet_protocol +{ +public: + /// Construct a protocol object for a specific address family and protocol. + seq_packet_protocol(int address_family, int socket_protocol) + : family_(address_family), + protocol_(socket_protocol) + { + } + + /// Construct a generic protocol object from a specific protocol. + /** + * @throws @c bad_cast Thrown if the source protocol is not based around + * sequenced packets. + */ + template + seq_packet_protocol(const Protocol& source_protocol) + : family_(source_protocol.family()), + protocol_(source_protocol.protocol()) + { + if (source_protocol.type() != type()) + { + std::bad_cast ex; + asio::detail::throw_exception(ex); + } + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return ASIO_OS_DEF(SOCK_SEQPACKET); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return protocol_; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// Compare two protocols for equality. + friend bool operator==(const seq_packet_protocol& p1, + const seq_packet_protocol& p2) + { + return p1.family_ == p2.family_ && p1.protocol_ == p2.protocol_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const seq_packet_protocol& p1, + const seq_packet_protocol& p2) + { + return !(p1 == p2); + } + + /// The type of an endpoint. + typedef basic_endpoint endpoint; + + /// The generic socket type. + typedef basic_seq_packet_socket socket; + +private: + int family_; + int protocol_; +}; + +} // namespace generic +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_GENERIC_SEQ_PACKET_PROTOCOL_HPP diff --git a/subprojects/asio/include/asio/generic/stream_protocol.hpp b/subprojects/asio/include/asio/generic/stream_protocol.hpp new file mode 100644 index 000000000..8349f25bc --- /dev/null +++ b/subprojects/asio/include/asio/generic/stream_protocol.hpp @@ -0,0 +1,127 @@ +// +// generic/stream_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_GENERIC_STREAM_PROTOCOL_HPP +#define ASIO_GENERIC_STREAM_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/generic/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace generic { + +/// Encapsulates the flags needed for a generic stream-oriented socket. +/** + * The asio::generic::stream_protocol class contains flags necessary for + * stream-oriented sockets of any address family and protocol. + * + * @par Examples + * Constructing using a native address family and socket protocol: + * @code stream_protocol p(AF_INET, IPPROTO_TCP); @endcode + * Constructing from a specific protocol type: + * @code stream_protocol p(asio::ip::tcp::v4()); @endcode + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class stream_protocol +{ +public: + /// Construct a protocol object for a specific address family and protocol. + stream_protocol(int address_family, int socket_protocol) + : family_(address_family), + protocol_(socket_protocol) + { + } + + /// Construct a generic protocol object from a specific protocol. + /** + * @throws @c bad_cast Thrown if the source protocol is not stream-oriented. + */ + template + stream_protocol(const Protocol& source_protocol) + : family_(source_protocol.family()), + protocol_(source_protocol.protocol()) + { + if (source_protocol.type() != type()) + { + std::bad_cast ex; + asio::detail::throw_exception(ex); + } + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return ASIO_OS_DEF(SOCK_STREAM); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return protocol_; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// Compare two protocols for equality. + friend bool operator==(const stream_protocol& p1, const stream_protocol& p2) + { + return p1.family_ == p2.family_ && p1.protocol_ == p2.protocol_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const stream_protocol& p1, const stream_protocol& p2) + { + return !(p1 == p2); + } + + /// The type of an endpoint. + typedef basic_endpoint endpoint; + + /// The generic socket type. + typedef basic_stream_socket socket; + +#if !defined(ASIO_NO_IOSTREAM) + /// The generic socket iostream type. + typedef basic_socket_iostream iostream; +#endif // !defined(ASIO_NO_IOSTREAM) + +private: + int family_; + int protocol_; +}; + +} // namespace generic +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_GENERIC_STREAM_PROTOCOL_HPP diff --git a/subprojects/asio/include/asio/handler_alloc_hook.hpp b/subprojects/asio/include/asio/handler_alloc_hook.hpp new file mode 100644 index 000000000..d7fe2b01f --- /dev/null +++ b/subprojects/asio/include/asio/handler_alloc_hook.hpp @@ -0,0 +1,81 @@ +// +// handler_alloc_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HANDLER_ALLOC_HOOK_HPP +#define ASIO_HANDLER_ALLOC_HOOK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default allocation function for handlers. +/** + * Asynchronous operations may need to allocate temporary objects. Since + * asynchronous operations have a handler function object, these temporary + * objects can be said to be associated with the handler. + * + * Implement asio_handler_allocate and asio_handler_deallocate for your own + * handlers to provide custom allocation for these temporary objects. + * + * The default implementation of these allocation hooks uses ::operator + * new and ::operator delete. + * + * @note All temporary objects associated with a handler will be deallocated + * before the upcall to the handler is performed. This allows the same memory to + * be reused for a subsequent asynchronous operation initiated by the handler. + * + * @par Example + * @code + * class my_handler; + * + * void* asio_handler_allocate(std::size_t size, my_handler* context) + * { + * return ::operator new(size); + * } + * + * void asio_handler_deallocate(void* pointer, std::size_t size, + * my_handler* context) + * { + * ::operator delete(pointer); + * } + * @endcode + */ +ASIO_DECL void* asio_handler_allocate( + std::size_t size, ...); + +/// Default deallocation function for handlers. +/** + * Implement asio_handler_allocate and asio_handler_deallocate for your own + * handlers to provide custom allocation for the associated temporary objects. + * + * The default implementation of these allocation hooks uses ::operator + * new and ::operator delete. + * + * @sa asio_handler_allocate. + */ +ASIO_DECL void asio_handler_deallocate( + void* pointer, std::size_t size, ...); + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/handler_alloc_hook.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_HANDLER_ALLOC_HOOK_HPP diff --git a/subprojects/asio/include/asio/handler_continuation_hook.hpp b/subprojects/asio/include/asio/handler_continuation_hook.hpp new file mode 100644 index 000000000..d41d10595 --- /dev/null +++ b/subprojects/asio/include/asio/handler_continuation_hook.hpp @@ -0,0 +1,54 @@ +// +// handler_continuation_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HANDLER_CONTINUATION_HOOK_HPP +#define ASIO_HANDLER_CONTINUATION_HOOK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default continuation function for handlers. +/** + * Asynchronous operations may represent a continuation of the asynchronous + * control flow associated with the current handler. The implementation can use + * this knowledge to optimise scheduling of the handler. + * + * Implement asio_handler_is_continuation for your own handlers to indicate + * when a handler represents a continuation. + * + * The default implementation of the continuation hook returns false. + * + * @par Example + * @code + * class my_handler; + * + * bool asio_handler_is_continuation(my_handler* context) + * { + * return true; + * } + * @endcode + */ +inline bool asio_handler_is_continuation(...) +{ + return false; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_HANDLER_CONTINUATION_HOOK_HPP diff --git a/subprojects/asio/include/asio/handler_invoke_hook.hpp b/subprojects/asio/include/asio/handler_invoke_hook.hpp new file mode 100644 index 000000000..ee618e5ef --- /dev/null +++ b/subprojects/asio/include/asio/handler_invoke_hook.hpp @@ -0,0 +1,85 @@ +// +// handler_invoke_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HANDLER_INVOKE_HOOK_HPP +#define ASIO_HANDLER_INVOKE_HOOK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/** @defgroup asio_handler_invoke asio::asio_handler_invoke + * + * @brief Default invoke function for handlers. + * + * Completion handlers for asynchronous operations are invoked by the + * io_context associated with the corresponding object (e.g. a socket or + * deadline_timer). Certain guarantees are made on when the handler may be + * invoked, in particular that a handler can only be invoked from a thread that + * is currently calling @c run() on the corresponding io_context object. + * Handlers may subsequently be invoked through other objects (such as + * io_context::strand objects) that provide additional guarantees. + * + * When asynchronous operations are composed from other asynchronous + * operations, all intermediate handlers should be invoked using the same + * method as the final handler. This is required to ensure that user-defined + * objects are not accessed in a way that may violate the guarantees. This + * hooking function ensures that the invoked method used for the final handler + * is accessible at each intermediate step. + * + * Implement asio_handler_invoke for your own handlers to specify a custom + * invocation strategy. + * + * This default implementation invokes the function object like so: + * @code function(); @endcode + * If necessary, the default implementation makes a copy of the function object + * so that the non-const operator() can be used. + * + * @par Example + * @code + * class my_handler; + * + * template + * void asio_handler_invoke(Function function, my_handler* context) + * { + * context->strand_.dispatch(function); + * } + * @endcode + */ +/*@{*/ + +/// Default handler invocation hook used for non-const function objects. +template +inline void asio_handler_invoke(Function& function, ...) +{ + function(); +} + +/// Default handler invocation hook used for const function objects. +template +inline void asio_handler_invoke(const Function& function, ...) +{ + Function tmp(function); + tmp(); +} + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_HANDLER_INVOKE_HOOK_HPP diff --git a/subprojects/asio/include/asio/handler_type.hpp b/subprojects/asio/include/asio/handler_type.hpp new file mode 100644 index 000000000..bc0d30514 --- /dev/null +++ b/subprojects/asio/include/asio/handler_type.hpp @@ -0,0 +1,50 @@ +// +// handler_type.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HANDLER_TYPE_HPP +#define ASIO_HANDLER_TYPE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// (Deprecated: Use two-parameter version of async_result.) Default handler +/// type traits provided for all completion token types. +/** + * The handler_type traits class is used for determining the concrete handler + * type to be used for an asynchronous operation. It allows the handler type to + * be determined at the point where the specific completion handler signature + * is known. + * + * This template may be specialised for user-defined completion token types. + */ +template +struct handler_type +{ + /// The handler type for the specific signature. + typedef typename conditional< + is_same::type>::value, + decay, + handler_type::type, Signature> + >::type::type type; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_HANDLER_TYPE_HPP diff --git a/subprojects/asio/include/asio/high_resolution_timer.hpp b/subprojects/asio/include/asio/high_resolution_timer.hpp new file mode 100644 index 000000000..0549cc256 --- /dev/null +++ b/subprojects/asio/include/asio/high_resolution_timer.hpp @@ -0,0 +1,44 @@ +// +// high_resolution_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HIGH_RESOLUTION_TIMER_HPP +#define ASIO_HIGH_RESOLUTION_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_waitable_timer.hpp" +#include "asio/detail/chrono.hpp" + +namespace asio { + +/// Typedef for a timer based on the high resolution clock. +/** + * This typedef uses the C++11 @c <chrono> standard library facility, if + * available. Otherwise, it may use the Boost.Chrono library. To explicitly + * utilise Boost.Chrono, use the basic_waitable_timer template directly: + * @code + * typedef basic_waitable_timer timer; + * @endcode + */ +typedef basic_waitable_timer< + chrono::high_resolution_clock> + high_resolution_timer; + +} // namespace asio + +#endif // defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_HIGH_RESOLUTION_TIMER_HPP diff --git a/subprojects/asio/include/asio/impl/buffered_read_stream.hpp b/subprojects/asio/include/asio/impl/buffered_read_stream.hpp new file mode 100644 index 000000000..e0ed20e4c --- /dev/null +++ b/subprojects/asio/include/asio/impl/buffered_read_stream.hpp @@ -0,0 +1,429 @@ +// +// impl/buffered_read_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_BUFFERED_READ_STREAM_HPP +#define ASIO_IMPL_BUFFERED_READ_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +std::size_t buffered_read_stream::fill() +{ + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size))); + resize_guard.commit(); + return storage_.size() - previous_size; +} + +template +std::size_t buffered_read_stream::fill(asio::error_code& ec) +{ + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + ec)); + resize_guard.commit(); + return storage_.size() - previous_size; +} + +namespace detail +{ + template + class buffered_fill_handler + { + public: + buffered_fill_handler(detail::buffered_stream_storage& storage, + std::size_t previous_size, ReadHandler& handler) + : storage_(storage), + previous_size_(previous_size), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + buffered_fill_handler(const buffered_fill_handler& other) + : storage_(other.storage_), + previous_size_(other.previous_size_), + handler_(other.handler_) + { + } + + buffered_fill_handler(buffered_fill_handler&& other) + : storage_(other.storage_), + previous_size_(other.previous_size_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + const std::size_t bytes_transferred) + { + storage_.resize(previous_size_ + bytes_transferred); + handler_(ec, bytes_transferred); + } + + //private: + detail::buffered_stream_storage& storage_; + std::size_t previous_size_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + buffered_fill_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + buffered_fill_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + buffered_fill_handler* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + buffered_fill_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + buffered_fill_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::buffered_fill_handler, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::buffered_fill_handler& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::buffered_fill_handler, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const detail::buffered_fill_handler& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +buffered_read_stream::async_fill( + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + next_layer_.async_read_some( + buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + detail::buffered_fill_handler( + storage_, previous_size, init.completion_handler)); + + return init.result.get(); +} + +template +template +std::size_t buffered_read_stream::read_some( + const MutableBufferSequence& buffers) +{ + using asio::buffer_size; + if (buffer_size(buffers) == 0) + return 0; + + if (storage_.empty()) + this->fill(); + + return this->copy(buffers); +} + +template +template +std::size_t buffered_read_stream::read_some( + const MutableBufferSequence& buffers, asio::error_code& ec) +{ + ec = asio::error_code(); + + using asio::buffer_size; + if (buffer_size(buffers) == 0) + return 0; + + if (storage_.empty() && !this->fill(ec)) + return 0; + + return this->copy(buffers); +} + +namespace detail +{ + template + class buffered_read_some_handler + { + public: + buffered_read_some_handler(detail::buffered_stream_storage& storage, + const MutableBufferSequence& buffers, ReadHandler& handler) + : storage_(storage), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + buffered_read_some_handler(const buffered_read_some_handler& other) + : storage_(other.storage_), + buffers_(other.buffers_), + handler_(other.handler_) + { + } + + buffered_read_some_handler(buffered_read_some_handler&& other) + : storage_(other.storage_), + buffers_(other.buffers_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, std::size_t) + { + if (ec || storage_.empty()) + { + const std::size_t length = 0; + handler_(ec, length); + } + else + { + const std::size_t bytes_copied = asio::buffer_copy( + buffers_, storage_.data(), storage_.size()); + storage_.consume(bytes_copied); + handler_(ec, bytes_copied); + } + } + + //private: + detail::buffered_stream_storage& storage_; + MutableBufferSequence buffers_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + buffered_read_some_handler< + MutableBufferSequence, ReadHandler>* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + buffered_read_some_handler< + MutableBufferSequence, ReadHandler>* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + buffered_read_some_handler< + MutableBufferSequence, ReadHandler>* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + buffered_read_some_handler< + MutableBufferSequence, ReadHandler>* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + buffered_read_some_handler< + MutableBufferSequence, ReadHandler>* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::buffered_read_some_handler, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::buffered_read_some_handler< + MutableBufferSequence, ReadHandler>& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::buffered_read_some_handler, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::buffered_read_some_handler< + MutableBufferSequence, ReadHandler>& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +buffered_read_stream::async_read_some( + const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + using asio::buffer_size; + if (buffer_size(buffers) == 0 || !storage_.empty()) + { + next_layer_.async_read_some(ASIO_MUTABLE_BUFFER(0, 0), + detail::buffered_read_some_handler< + MutableBufferSequence, ASIO_HANDLER_TYPE( + ReadHandler, void (asio::error_code, std::size_t))>( + storage_, buffers, init.completion_handler)); + } + else + { + this->async_fill(detail::buffered_read_some_handler< + MutableBufferSequence, ASIO_HANDLER_TYPE( + ReadHandler, void (asio::error_code, std::size_t))>( + storage_, buffers, init.completion_handler)); + } + + return init.result.get(); +} + +template +template +std::size_t buffered_read_stream::peek( + const MutableBufferSequence& buffers) +{ + if (storage_.empty()) + this->fill(); + return this->peek_copy(buffers); +} + +template +template +std::size_t buffered_read_stream::peek( + const MutableBufferSequence& buffers, asio::error_code& ec) +{ + ec = asio::error_code(); + if (storage_.empty() && !this->fill(ec)) + return 0; + return this->peek_copy(buffers); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_BUFFERED_READ_STREAM_HPP diff --git a/subprojects/asio/include/asio/impl/buffered_write_stream.hpp b/subprojects/asio/include/asio/impl/buffered_write_stream.hpp new file mode 100644 index 000000000..bc2d823ab --- /dev/null +++ b/subprojects/asio/include/asio/impl/buffered_write_stream.hpp @@ -0,0 +1,411 @@ +// +// impl/buffered_write_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_BUFFERED_WRITE_STREAM_HPP +#define ASIO_IMPL_BUFFERED_WRITE_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +std::size_t buffered_write_stream::flush() +{ + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size())); + storage_.consume(bytes_written); + return bytes_written; +} + +template +std::size_t buffered_write_stream::flush(asio::error_code& ec) +{ + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size()), + transfer_all(), ec); + storage_.consume(bytes_written); + return bytes_written; +} + +namespace detail +{ + template + class buffered_flush_handler + { + public: + buffered_flush_handler(detail::buffered_stream_storage& storage, + WriteHandler& handler) + : storage_(storage), + handler_(ASIO_MOVE_CAST(WriteHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + buffered_flush_handler(const buffered_flush_handler& other) + : storage_(other.storage_), + handler_(other.handler_) + { + } + + buffered_flush_handler(buffered_flush_handler&& other) + : storage_(other.storage_), + handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + const std::size_t bytes_written) + { + storage_.consume(bytes_written); + handler_(ec, bytes_written); + } + + //private: + detail::buffered_stream_storage& storage_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + buffered_flush_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + buffered_flush_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + buffered_flush_handler* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + buffered_flush_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + buffered_flush_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::buffered_flush_handler, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::buffered_flush_handler& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::buffered_flush_handler, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const detail::buffered_flush_handler& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +buffered_write_stream::async_flush( + ASIO_MOVE_ARG(WriteHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + async_write(next_layer_, buffer(storage_.data(), storage_.size()), + detail::buffered_flush_handler( + storage_, init.completion_handler)); + + return init.result.get(); +} + +template +template +std::size_t buffered_write_stream::write_some( + const ConstBufferSequence& buffers) +{ + using asio::buffer_size; + if (buffer_size(buffers) == 0) + return 0; + + if (storage_.size() == storage_.capacity()) + this->flush(); + + return this->copy(buffers); +} + +template +template +std::size_t buffered_write_stream::write_some( + const ConstBufferSequence& buffers, asio::error_code& ec) +{ + ec = asio::error_code(); + + using asio::buffer_size; + if (buffer_size(buffers) == 0) + return 0; + + if (storage_.size() == storage_.capacity() && !flush(ec)) + return 0; + + return this->copy(buffers); +} + +namespace detail +{ + template + class buffered_write_some_handler + { + public: + buffered_write_some_handler(detail::buffered_stream_storage& storage, + const ConstBufferSequence& buffers, WriteHandler& handler) + : storage_(storage), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(WriteHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + buffered_write_some_handler(const buffered_write_some_handler& other) + : storage_(other.storage_), + buffers_(other.buffers_), + handler_(other.handler_) + { + } + + buffered_write_some_handler(buffered_write_some_handler&& other) + : storage_(other.storage_), + buffers_(other.buffers_), + handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, std::size_t) + { + if (ec) + { + const std::size_t length = 0; + handler_(ec, length); + } + else + { + using asio::buffer_size; + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_avail = buffer_size(buffers_); + std::size_t length = bytes_avail < space_avail + ? bytes_avail : space_avail; + storage_.resize(orig_size + length); + const std::size_t bytes_copied = asio::buffer_copy( + storage_.data() + orig_size, buffers_, length); + handler_(ec, bytes_copied); + } + } + + //private: + detail::buffered_stream_storage& storage_; + ConstBufferSequence buffers_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + buffered_write_some_handler< + ConstBufferSequence, WriteHandler>* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + buffered_write_some_handler< + ConstBufferSequence, WriteHandler>* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + buffered_write_some_handler< + ConstBufferSequence, WriteHandler>* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + buffered_write_some_handler< + ConstBufferSequence, WriteHandler>* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + buffered_write_some_handler< + ConstBufferSequence, WriteHandler>* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::buffered_write_some_handler, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::buffered_write_some_handler< + ConstBufferSequence, WriteHandler>& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::buffered_write_some_handler, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::buffered_write_some_handler< + ConstBufferSequence, WriteHandler>& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +buffered_write_stream::async_write_some( + const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + using asio::buffer_size; + if (buffer_size(buffers) == 0 + || storage_.size() < storage_.capacity()) + { + next_layer_.async_write_some(ASIO_CONST_BUFFER(0, 0), + detail::buffered_write_some_handler< + ConstBufferSequence, ASIO_HANDLER_TYPE( + WriteHandler, void (asio::error_code, std::size_t))>( + storage_, buffers, init.completion_handler)); + } + else + { + this->async_flush(detail::buffered_write_some_handler< + ConstBufferSequence, ASIO_HANDLER_TYPE( + WriteHandler, void (asio::error_code, std::size_t))>( + storage_, buffers, init.completion_handler)); + } + + return init.result.get(); +} + +template +template +std::size_t buffered_write_stream::copy( + const ConstBufferSequence& buffers) +{ + using asio::buffer_size; + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_avail = buffer_size(buffers); + std::size_t length = bytes_avail < space_avail ? bytes_avail : space_avail; + storage_.resize(orig_size + length); + return asio::buffer_copy( + storage_.data() + orig_size, buffers, length); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_BUFFERED_WRITE_STREAM_HPP diff --git a/subprojects/asio/include/asio/impl/connect.hpp b/subprojects/asio/include/asio/impl/connect.hpp new file mode 100644 index 000000000..bff1913b1 --- /dev/null +++ b/subprojects/asio/include/asio/impl/connect.hpp @@ -0,0 +1,860 @@ +// +// impl/connect.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_CONNECT_HPP +#define ASIO_IMPL_CONNECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/post.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + struct default_connect_condition + { + template + bool operator()(const asio::error_code&, const Endpoint&) + { + return true; + } + }; + + template + inline typename Protocol::endpoint deref_connect_result( + Iterator iter, asio::error_code& ec) + { + return ec ? typename Protocol::endpoint() : *iter; + } + + template + struct legacy_connect_condition_helper : T + { + typedef char (*fallback_func_type)(...); + operator fallback_func_type() const; + }; + + template + struct legacy_connect_condition_helper + { + R operator()(Arg1, Arg2) const; + char operator()(...) const; + }; + + template + struct is_legacy_connect_condition + { + static char asio_connect_condition_check(char); + static char (&asio_connect_condition_check(Iterator))[2]; + + static const bool value = + sizeof(asio_connect_condition_check( + (*static_cast*>(0))( + *static_cast(0), + *static_cast(0)))) != 1; + }; + + template + inline Iterator call_connect_condition(ConnectCondition& connect_condition, + const asio::error_code& ec, Iterator next, Iterator end, + typename enable_if::value>::type* = 0) + { + if (next != end) + return connect_condition(ec, next); + return end; + } + + template + inline Iterator call_connect_condition(ConnectCondition& connect_condition, + const asio::error_code& ec, Iterator next, Iterator end, + typename enable_if::value>::type* = 0) + { + for (;next != end; ++next) + if (connect_condition(ec, *next)) + return next; + return end; + } +} + +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, + typename enable_if::value>::type*) +{ + asio::error_code ec; + typename Protocol::endpoint result = connect(s, endpoints, ec); + asio::detail::throw_error(ec, "connect"); + return result; +} + +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, asio::error_code& ec, + typename enable_if::value>::type*) +{ + return detail::deref_connect_result( + connect(s, endpoints.begin(), endpoints.end(), + detail::default_connect_condition(), ec), ec); +} + +#if !defined(ASIO_NO_DEPRECATED) +template +Iterator connect(basic_socket& s, Iterator begin, + typename enable_if::value>::type*) +{ + asio::error_code ec; + Iterator result = connect(s, begin, ec); + asio::detail::throw_error(ec, "connect"); + return result; +} + +template +inline Iterator connect(basic_socket& s, + Iterator begin, asio::error_code& ec, + typename enable_if::value>::type*) +{ + return connect(s, begin, Iterator(), detail::default_connect_condition(), ec); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end) +{ + asio::error_code ec; + Iterator result = connect(s, begin, end, ec); + asio::detail::throw_error(ec, "connect"); + return result; +} + +template +inline Iterator connect(basic_socket& s, + Iterator begin, Iterator end, asio::error_code& ec) +{ + return connect(s, begin, end, detail::default_connect_condition(), ec); +} + +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + typename enable_if::value>::type*) +{ + asio::error_code ec; + typename Protocol::endpoint result = connect( + s, endpoints, connect_condition, ec); + asio::detail::throw_error(ec, "connect"); + return result; +} + +template +typename Protocol::endpoint connect( + basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + asio::error_code& ec, + typename enable_if::value>::type*) +{ + return detail::deref_connect_result( + connect(s, endpoints.begin(), endpoints.end(), + connect_condition, ec), ec); +} + +#if !defined(ASIO_NO_DEPRECATED) +template +Iterator connect(basic_socket& s, + Iterator begin, ConnectCondition connect_condition, + typename enable_if::value>::type*) +{ + asio::error_code ec; + Iterator result = connect(s, begin, connect_condition, ec); + asio::detail::throw_error(ec, "connect"); + return result; +} + +template +inline Iterator connect(basic_socket& s, + Iterator begin, ConnectCondition connect_condition, + asio::error_code& ec, + typename enable_if::value>::type*) +{ + return connect(s, begin, Iterator(), connect_condition, ec); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end, ConnectCondition connect_condition) +{ + asio::error_code ec; + Iterator result = connect(s, begin, end, connect_condition, ec); + asio::detail::throw_error(ec, "connect"); + return result; +} + +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end, ConnectCondition connect_condition, + asio::error_code& ec) +{ + ec = asio::error_code(); + + for (Iterator iter = begin; iter != end; ++iter) + { + iter = (detail::call_connect_condition(connect_condition, ec, iter, end)); + if (iter != end) + { + s.close(ec); + s.connect(*iter, ec); + if (!ec) + return iter; + } + else + break; + } + + if (!ec) + ec = asio::error::not_found; + + return end; +} + +namespace detail +{ + // Enable the empty base class optimisation for the connect condition. + template + class base_from_connect_condition + { + protected: + explicit base_from_connect_condition( + const ConnectCondition& connect_condition) + : connect_condition_(connect_condition) + { + } + + template + void check_condition(const asio::error_code& ec, + Iterator& iter, Iterator& end) + { + iter = detail::call_connect_condition(connect_condition_, ec, iter, end); + } + + private: + ConnectCondition connect_condition_; + }; + + // The default_connect_condition implementation is essentially a no-op. This + // template specialisation lets us eliminate all costs associated with it. + template <> + class base_from_connect_condition + { + protected: + explicit base_from_connect_condition(const default_connect_condition&) + { + } + + template + void check_condition(const asio::error_code&, Iterator&, Iterator&) + { + } + }; + + template + class range_connect_op : base_from_connect_condition + { + public: + range_connect_op(basic_socket& sock, + const EndpointSequence& endpoints, + const ConnectCondition& connect_condition, + RangeConnectHandler& handler) + : base_from_connect_condition(connect_condition), + socket_(sock), + endpoints_(endpoints), + index_(0), + start_(0), + handler_(ASIO_MOVE_CAST(RangeConnectHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + range_connect_op(const range_connect_op& other) + : base_from_connect_condition(other), + socket_(other.socket_), + endpoints_(other.endpoints_), + index_(other.index_), + start_(other.start_), + handler_(other.handler_) + { + } + + range_connect_op(range_connect_op&& other) + : base_from_connect_condition(other), + socket_(other.socket_), + endpoints_(other.endpoints_), + index_(other.index_), + start_(other.start_), + handler_(ASIO_MOVE_CAST(RangeConnectHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(asio::error_code ec, int start = 0) + { + typename EndpointSequence::const_iterator begin = endpoints_.begin(); + typename EndpointSequence::const_iterator iter = begin; + std::advance(iter, index_); + typename EndpointSequence::const_iterator end = endpoints_.end(); + + switch (start_ = start) + { + case 1: + for (;;) + { + this->check_condition(ec, iter, end); + index_ = std::distance(begin, iter); + + if (iter != end) + { + socket_.close(ec); + socket_.async_connect(*iter, + ASIO_MOVE_CAST(range_connect_op)(*this)); + return; + } + + if (start) + { + ec = asio::error::not_found; + asio::post(socket_.get_executor(), + detail::bind_handler( + ASIO_MOVE_CAST(range_connect_op)(*this), ec)); + return; + } + + default: + + if (iter == end) + break; + + if (!socket_.is_open()) + { + ec = asio::error::operation_aborted; + break; + } + + if (!ec) + break; + + ++iter; + ++index_; + } + + handler_(static_cast(ec), + static_cast( + ec || iter == end ? typename Protocol::endpoint() : *iter)); + } + } + + //private: + basic_socket& socket_; + EndpointSequence endpoints_; + std::size_t index_; + int start_; + RangeConnectHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + range_connect_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + range_connect_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + range_connect_op* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + range_connect_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + range_connect_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + class iterator_connect_op : base_from_connect_condition + { + public: + iterator_connect_op(basic_socket& sock, + const Iterator& begin, const Iterator& end, + const ConnectCondition& connect_condition, + IteratorConnectHandler& handler) + : base_from_connect_condition(connect_condition), + socket_(sock), + iter_(begin), + end_(end), + start_(0), + handler_(ASIO_MOVE_CAST(IteratorConnectHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + iterator_connect_op(const iterator_connect_op& other) + : base_from_connect_condition(other), + socket_(other.socket_), + iter_(other.iter_), + end_(other.end_), + start_(other.start_), + handler_(other.handler_) + { + } + + iterator_connect_op(iterator_connect_op&& other) + : base_from_connect_condition(other), + socket_(other.socket_), + iter_(other.iter_), + end_(other.end_), + start_(other.start_), + handler_(ASIO_MOVE_CAST(IteratorConnectHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(asio::error_code ec, int start = 0) + { + switch (start_ = start) + { + case 1: + for (;;) + { + this->check_condition(ec, iter_, end_); + + if (iter_ != end_) + { + socket_.close(ec); + socket_.async_connect(*iter_, + ASIO_MOVE_CAST(iterator_connect_op)(*this)); + return; + } + + if (start) + { + ec = asio::error::not_found; + asio::post(socket_.get_executor(), + detail::bind_handler( + ASIO_MOVE_CAST(iterator_connect_op)(*this), ec)); + return; + } + + default: + + if (iter_ == end_) + break; + + if (!socket_.is_open()) + { + ec = asio::error::operation_aborted; + break; + } + + if (!ec) + break; + + ++iter_; + } + + handler_(static_cast(ec), + static_cast(iter_)); + } + } + + //private: + basic_socket& socket_; + Iterator iter_; + Iterator end_; + int start_; + IteratorConnectHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + iterator_connect_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + iterator_connect_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + iterator_connect_op* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + iterator_connect_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + iterator_connect_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::range_connect_op, + Allocator> +{ + typedef typename associated_allocator< + RangeConnectHandler, Allocator>::type type; + + static type get( + const detail::range_connect_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::range_connect_op, + Executor> +{ + typedef typename associated_executor< + RangeConnectHandler, Executor>::type type; + + static type get( + const detail::range_connect_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +template +struct associated_allocator< + detail::iterator_connect_op, + Allocator> +{ + typedef typename associated_allocator< + IteratorConnectHandler, Allocator>::type type; + + static type get( + const detail::iterator_connect_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::iterator_connect_op, + Executor> +{ + typedef typename associated_executor< + IteratorConnectHandler, Executor>::type type; + + static type get( + const detail::iterator_connect_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, + void (asio::error_code, typename Protocol::endpoint)) +async_connect(basic_socket& s, + const EndpointSequence& endpoints, + ASIO_MOVE_ARG(RangeConnectHandler) handler, + typename enable_if::value>::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a RangeConnectHandler. + ASIO_RANGE_CONNECT_HANDLER_CHECK( + RangeConnectHandler, handler, typename Protocol::endpoint) type_check; + + async_completion + init(handler); + + detail::range_connect_op(s, + endpoints, detail::default_connect_condition(), + init.completion_handler)(asio::error_code(), 1); + + return init.result.get(); +} + +#if !defined(ASIO_NO_DEPRECATED) +template +inline ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, + Iterator begin, ASIO_MOVE_ARG(IteratorConnectHandler) handler, + typename enable_if::value>::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a IteratorConnectHandler. + ASIO_ITERATOR_CONNECT_HANDLER_CHECK( + IteratorConnectHandler, handler, Iterator) type_check; + + async_completion init(handler); + + detail::iterator_connect_op(s, + begin, Iterator(), detail::default_connect_condition(), + init.completion_handler)(asio::error_code(), 1); + + return init.result.get(); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +template +inline ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, + Iterator begin, Iterator end, + ASIO_MOVE_ARG(IteratorConnectHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a IteratorConnectHandler. + ASIO_ITERATOR_CONNECT_HANDLER_CHECK( + IteratorConnectHandler, handler, Iterator) type_check; + + async_completion init(handler); + + detail::iterator_connect_op(s, + begin, end, detail::default_connect_condition(), + init.completion_handler)(asio::error_code(), 1); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, + void (asio::error_code, typename Protocol::endpoint)) +async_connect(basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + ASIO_MOVE_ARG(RangeConnectHandler) handler, + typename enable_if::value>::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a RangeConnectHandler. + ASIO_RANGE_CONNECT_HANDLER_CHECK( + RangeConnectHandler, handler, typename Protocol::endpoint) type_check; + + async_completion + init(handler); + + detail::range_connect_op(s, + endpoints, connect_condition, init.completion_handler)( + asio::error_code(), 1); + + return init.result.get(); +} + +#if !defined(ASIO_NO_DEPRECATED) +template +inline ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, + Iterator begin, ConnectCondition connect_condition, + ASIO_MOVE_ARG(IteratorConnectHandler) handler, + typename enable_if::value>::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a IteratorConnectHandler. + ASIO_ITERATOR_CONNECT_HANDLER_CHECK( + IteratorConnectHandler, handler, Iterator) type_check; + + async_completion init(handler); + + detail::iterator_connect_op(s, + begin, Iterator(), connect_condition, init.completion_handler)( + asio::error_code(), 1); + + return init.result.get(); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +template +inline ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, + Iterator begin, Iterator end, ConnectCondition connect_condition, + ASIO_MOVE_ARG(IteratorConnectHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a IteratorConnectHandler. + ASIO_ITERATOR_CONNECT_HANDLER_CHECK( + IteratorConnectHandler, handler, Iterator) type_check; + + async_completion init(handler); + + detail::iterator_connect_op(s, + begin, end, connect_condition, init.completion_handler)( + asio::error_code(), 1); + + return init.result.get(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_CONNECT_HPP diff --git a/subprojects/asio/include/asio/impl/defer.hpp b/subprojects/asio/include/asio/impl/defer.hpp new file mode 100644 index 000000000..a27df0f13 --- /dev/null +++ b/subprojects/asio/include/asio/impl/defer.hpp @@ -0,0 +1,77 @@ +// +// impl/defer.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_DEFER_HPP +#define ASIO_IMPL_DEFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/work_dispatcher.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) defer( + ASIO_MOVE_ARG(CompletionToken) token) +{ + typedef ASIO_HANDLER_TYPE(CompletionToken, void()) handler; + + async_completion init(token); + + typename associated_executor::type ex( + (get_associated_executor)(init.completion_handler)); + + typename associated_allocator::type alloc( + (get_associated_allocator)(init.completion_handler)); + + ex.defer(ASIO_MOVE_CAST(handler)(init.completion_handler), alloc); + + return init.result.get(); +} + +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) defer( + const Executor& ex, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type*) +{ + typedef ASIO_HANDLER_TYPE(CompletionToken, void()) handler; + + async_completion init(token); + + typename associated_allocator::type alloc( + (get_associated_allocator)(init.completion_handler)); + + ex.defer(detail::work_dispatcher(init.completion_handler), alloc); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) defer( + ExecutionContext& ctx, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type*) +{ + return (defer)(ctx.get_executor(), + ASIO_MOVE_CAST(CompletionToken)(token)); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_DEFER_HPP diff --git a/subprojects/asio/include/asio/impl/dispatch.hpp b/subprojects/asio/include/asio/impl/dispatch.hpp new file mode 100644 index 000000000..4e11c6b2e --- /dev/null +++ b/subprojects/asio/include/asio/impl/dispatch.hpp @@ -0,0 +1,78 @@ +// +// impl/dispatch.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_DISPATCH_HPP +#define ASIO_IMPL_DISPATCH_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/work_dispatcher.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) dispatch( + ASIO_MOVE_ARG(CompletionToken) token) +{ + typedef ASIO_HANDLER_TYPE(CompletionToken, void()) handler; + + async_completion init(token); + + typename associated_executor::type ex( + (get_associated_executor)(init.completion_handler)); + + typename associated_allocator::type alloc( + (get_associated_allocator)(init.completion_handler)); + + ex.dispatch(ASIO_MOVE_CAST(handler)(init.completion_handler), alloc); + + return init.result.get(); +} + +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) dispatch( + const Executor& ex, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type*) +{ + typedef ASIO_HANDLER_TYPE(CompletionToken, void()) handler; + + async_completion init(token); + + typename associated_allocator::type alloc( + (get_associated_allocator)(init.completion_handler)); + + ex.dispatch(detail::work_dispatcher( + init.completion_handler), alloc); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) dispatch( + ExecutionContext& ctx, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type*) +{ + return (dispatch)(ctx.get_executor(), + ASIO_MOVE_CAST(CompletionToken)(token)); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_DISPATCH_HPP diff --git a/subprojects/asio/include/asio/impl/error.ipp b/subprojects/asio/include/asio/impl/error.ipp new file mode 100644 index 000000000..6b1f33a4c --- /dev/null +++ b/subprojects/asio/include/asio/impl/error.ipp @@ -0,0 +1,128 @@ +// +// impl/error.ipp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_ERROR_IPP +#define ASIO_IMPL_ERROR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace error { + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +namespace detail { + +class netdb_category : public asio::error_category +{ +public: + const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT + { + return "asio.netdb"; + } + + std::string message(int value) const + { + if (value == error::host_not_found) + return "Host not found (authoritative)"; + if (value == error::host_not_found_try_again) + return "Host not found (non-authoritative), try again later"; + if (value == error::no_data) + return "The query is valid, but it does not have associated data"; + if (value == error::no_recovery) + return "A non-recoverable error occurred during database lookup"; + return "asio.netdb error"; + } +}; + +} // namespace detail + +const asio::error_category& get_netdb_category() +{ + static detail::netdb_category instance; + return instance; +} + +namespace detail { + +class addrinfo_category : public asio::error_category +{ +public: + const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT + { + return "asio.addrinfo"; + } + + std::string message(int value) const + { + if (value == error::service_not_found) + return "Service not found"; + if (value == error::socket_type_not_supported) + return "Socket type not supported"; + return "asio.addrinfo error"; + } +}; + +} // namespace detail + +const asio::error_category& get_addrinfo_category() +{ + static detail::addrinfo_category instance; + return instance; +} + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +namespace detail { + +class misc_category : public asio::error_category +{ +public: + const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT + { + return "asio.misc"; + } + + std::string message(int value) const + { + if (value == error::already_open) + return "Already open"; + if (value == error::eof) + return "End of file"; + if (value == error::not_found) + return "Element not found"; + if (value == error::fd_set_failure) + return "The descriptor does not fit into the select call's fd_set"; + return "asio.misc error"; + } +}; + +} // namespace detail + +const asio::error_category& get_misc_category() +{ + static detail::misc_category instance; + return instance; +} + +} // namespace error +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_ERROR_IPP diff --git a/subprojects/asio/include/asio/impl/error_code.ipp b/subprojects/asio/include/asio/impl/error_code.ipp new file mode 100644 index 000000000..0c8a82782 --- /dev/null +++ b/subprojects/asio/include/asio/impl/error_code.ipp @@ -0,0 +1,206 @@ +// +// impl/error_code.ipp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_ERROR_CODE_IPP +#define ASIO_IMPL_ERROR_CODE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# include +#elif defined(ASIO_WINDOWS_RUNTIME) +# include +#else +# include +# include +# include +#endif +#include "asio/detail/local_free_on_block_exit.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class system_category : public error_category +{ +public: + const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT + { + return "asio.system"; + } + + std::string message(int value) const + { +#if defined(ASIO_WINDOWS_RUNTIME) || defined(ASIO_WINDOWS_APP) + std::wstring wmsg(128, wchar_t()); + for (;;) + { + DWORD wlength = ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, value, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + &wmsg[0], static_cast(wmsg.size()), 0); + if (wlength == 0 && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + wmsg.resize(wmsg.size() + wmsg.size() / 2); + continue; + } + if (wlength && wmsg[wlength - 1] == '\n') + --wlength; + if (wlength && wmsg[wlength - 1] == '\r') + --wlength; + if (wlength) + { + std::string msg(wlength * 2, char()); + int length = ::WideCharToMultiByte(CP_ACP, 0, + wmsg.c_str(), static_cast(wlength), + &msg[0], static_cast(wlength * 2), 0, 0); + if (length <= 0) + return "asio.system error"; + msg.resize(static_cast(length)); + return msg; + } + else + return "asio.system error"; + } +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + char* msg = 0; + DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, value, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + detail::local_free_on_block_exit local_free_obj(msg); + if (length && msg[length - 1] == '\n') + msg[--length] = '\0'; + if (length && msg[length - 1] == '\r') + msg[--length] = '\0'; + if (length) + return msg; + else + return "asio.system error"; +#else // defined(ASIO_WINDOWS_DESKTOP) || defined(__CYGWIN__) +#if !defined(__sun) + if (value == ECANCELED) + return "Operation aborted."; +#endif // !defined(__sun) +#if defined(__sun) || defined(__QNX__) || defined(__SYMBIAN32__) + using namespace std; + return strerror(value); +#else + char buf[256] = ""; + using namespace std; + return strerror_result(strerror_r(value, buf, sizeof(buf)), buf); +#endif +#endif // defined(ASIO_WINDOWS_DESKTOP) || defined(__CYGWIN__) + } + +#if defined(ASIO_HAS_STD_ERROR_CODE) + std::error_condition default_error_condition( + int ev) const ASIO_ERROR_CATEGORY_NOEXCEPT + { + switch (ev) + { + case access_denied: + return std::errc::permission_denied; + case address_family_not_supported: + return std::errc::address_family_not_supported; + case address_in_use: + return std::errc::address_in_use; + case already_connected: + return std::errc::already_connected; + case already_started: + return std::errc::connection_already_in_progress; + case broken_pipe: + return std::errc::broken_pipe; + case connection_aborted: + return std::errc::connection_aborted; + case connection_refused: + return std::errc::connection_refused; + case connection_reset: + return std::errc::connection_reset; + case bad_descriptor: + return std::errc::bad_file_descriptor; + case fault: + return std::errc::bad_address; + case host_unreachable: + return std::errc::host_unreachable; + case in_progress: + return std::errc::operation_in_progress; + case interrupted: + return std::errc::interrupted; + case invalid_argument: + return std::errc::invalid_argument; + case message_size: + return std::errc::message_size; + case name_too_long: + return std::errc::filename_too_long; + case network_down: + return std::errc::network_down; + case network_reset: + return std::errc::network_reset; + case network_unreachable: + return std::errc::network_unreachable; + case no_descriptors: + return std::errc::too_many_files_open; + case no_buffer_space: + return std::errc::no_buffer_space; + case no_memory: + return std::errc::not_enough_memory; + case no_permission: + return std::errc::operation_not_permitted; + case no_protocol_option: + return std::errc::no_protocol_option; + case no_such_device: + return std::errc::no_such_device; + case not_connected: + return std::errc::not_connected; + case not_socket: + return std::errc::not_a_socket; + case operation_aborted: + return std::errc::operation_canceled; + case operation_not_supported: + return std::errc::operation_not_supported; + case shut_down: + return std::make_error_condition(ev, *this); + case timed_out: + return std::errc::timed_out; + case try_again: + return std::errc::resource_unavailable_try_again; + case would_block: + return std::errc::operation_would_block; + default: + return std::make_error_condition(ev, *this); + } +#endif // defined(ASIO_HAS_STD_ERROR_CODE) + +private: + // Helper function to adapt the result from glibc's variant of strerror_r. + static const char* strerror_result(int, const char* s) { return s; } + static const char* strerror_result(const char* s, const char*) { return s; } +}; + +} // namespace detail + +const error_category& system_category() +{ + static detail::system_category instance; + return instance; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_ERROR_CODE_IPP diff --git a/subprojects/asio/include/asio/impl/execution_context.hpp b/subprojects/asio/include/asio/impl/execution_context.hpp new file mode 100644 index 000000000..3d1e457e7 --- /dev/null +++ b/subprojects/asio/include/asio/impl/execution_context.hpp @@ -0,0 +1,107 @@ +// +// impl/execution_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_EXECUTION_CONTEXT_HPP +#define ASIO_IMPL_EXECUTION_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/service_registry.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +inline Service& use_service(execution_context& e) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + + return e.service_registry_->template use_service(); +} + +#if !defined(GENERATING_DOCUMENTATION) +# if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +Service& make_service(execution_context& e, ASIO_MOVE_ARG(Args)... args) +{ + detail::scoped_ptr svc( + new Service(e, ASIO_MOVE_CAST(Args)(args)...)); + e.service_registry_->template add_service(svc.get()); + Service& result = *svc; + svc.release(); + return result; +} + +# else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +Service& make_service(execution_context& e) +{ + detail::scoped_ptr svc(new Service(e)); + e.service_registry_->template add_service(svc.get()); + Service& result = *svc; + svc.release(); + return result; +} + +#define ASIO_PRIVATE_MAKE_SERVICE_DEF(n) \ + template \ + Service& make_service(execution_context& e, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + detail::scoped_ptr svc( \ + new Service(e, ASIO_VARIADIC_MOVE_ARGS(n))); \ + e.service_registry_->template add_service(svc.get()); \ + Service& result = *svc; \ + svc.release(); \ + return result; \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_MAKE_SERVICE_DEF) +#undef ASIO_PRIVATE_MAKE_SERVICE_DEF + +# endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline void add_service(execution_context& e, Service* svc) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + + e.service_registry_->template add_service(svc); +} + +template +inline bool has_service(execution_context& e) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + + return e.service_registry_->template has_service(); +} + +inline execution_context& execution_context::service::context() +{ + return owner_; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_EXECUTION_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/impl/execution_context.ipp b/subprojects/asio/include/asio/impl/execution_context.ipp new file mode 100644 index 000000000..c2b3b21a3 --- /dev/null +++ b/subprojects/asio/include/asio/impl/execution_context.ipp @@ -0,0 +1,82 @@ +// +// impl/execution_context.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_EXECUTION_CONTEXT_IPP +#define ASIO_IMPL_EXECUTION_CONTEXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/service_registry.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +execution_context::execution_context() + : service_registry_(new asio::detail::service_registry(*this)) +{ +} + +execution_context::~execution_context() +{ + shutdown(); + destroy(); + delete service_registry_; +} + +void execution_context::shutdown() +{ + service_registry_->shutdown_services(); +} + +void execution_context::destroy() +{ + service_registry_->destroy_services(); +} + +void execution_context::notify_fork( + asio::execution_context::fork_event event) +{ + service_registry_->notify_fork(event); +} + +execution_context::service::service(execution_context& owner) + : owner_(owner), + next_(0) +{ +} + +execution_context::service::~service() +{ +} + +void execution_context::service::notify_fork(execution_context::fork_event) +{ +} + +service_already_exists::service_already_exists() + : std::logic_error("Service already exists.") +{ +} + +invalid_service_owner::invalid_service_owner() + : std::logic_error("Invalid service owner.") +{ +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_EXECUTION_CONTEXT_IPP diff --git a/subprojects/asio/include/asio/impl/executor.hpp b/subprojects/asio/include/asio/impl/executor.hpp new file mode 100644 index 000000000..0fcf5f54a --- /dev/null +++ b/subprojects/asio/include/asio/impl/executor.hpp @@ -0,0 +1,386 @@ +// +// impl/executor.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_EXECUTOR_HPP +#define ASIO_IMPL_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/executor_op.hpp" +#include "asio/detail/global.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/executor.hpp" +#include "asio/system_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_HAS_MOVE) + +// Lightweight, move-only function object wrapper. +class executor::function +{ +public: + template + explicit function(F f, const Alloc& a) + { + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + op_ = new (p.v) op(ASIO_MOVE_CAST(F)(f), a); + p.v = 0; + } + + function(function&& other) + : op_(other.op_) + { + other.op_ = 0; + } + + ~function() + { + if (op_) + op_->destroy(); + } + + void operator()() + { + if (op_) + { + detail::scheduler_operation* op = op_; + op_ = 0; + op->complete(this, asio::error_code(), 0); + } + } + +private: + detail::scheduler_operation* op_; +}; + +#else // defined(ASIO_HAS_MOVE) + +// Not so lightweight, copyable function object wrapper. +class executor::function +{ +public: + template + explicit function(const F& f, const Alloc&) + : impl_(new impl(f)) + { + } + + void operator()() + { + impl_->invoke_(impl_.get()); + } + +private: + // Base class for polymorphic function implementations. + struct impl_base + { + void (*invoke_)(impl_base*); + }; + + // Polymorphic function implementation. + template + struct impl : impl_base + { + impl(const F& f) + : function_(f) + { + invoke_ = &function::invoke; + } + + F function_; + }; + + // Helper to invoke a function. + template + static void invoke(impl_base* i) + { + static_cast*>(i)->function_(); + } + + detail::shared_ptr impl_; +}; + +#endif // defined(ASIO_HAS_MOVE) + +// Default polymorphic allocator implementation. +template +class executor::impl + : public executor::impl_base +{ +public: + typedef ASIO_REBIND_ALLOC(Allocator, impl) allocator_type; + + static impl_base* create(const Executor& e, Allocator a = Allocator()) + { + raw_mem mem(a); + impl* p = new (mem.ptr_) impl(e, a); + mem.ptr_ = 0; + return p; + } + + impl(const Executor& e, const Allocator& a) ASIO_NOEXCEPT + : impl_base(false), + ref_count_(1), + executor_(e), + allocator_(a) + { + } + + impl_base* clone() const ASIO_NOEXCEPT + { + ++ref_count_; + return const_cast(static_cast(this)); + } + + void destroy() ASIO_NOEXCEPT + { + if (--ref_count_ == 0) + { + allocator_type alloc(allocator_); + impl* p = this; + p->~impl(); + alloc.deallocate(p, 1); + } + } + + void on_work_started() ASIO_NOEXCEPT + { + executor_.on_work_started(); + } + + void on_work_finished() ASIO_NOEXCEPT + { + executor_.on_work_finished(); + } + + execution_context& context() ASIO_NOEXCEPT + { + return executor_.context(); + } + + void dispatch(ASIO_MOVE_ARG(function) f) + { + executor_.dispatch(ASIO_MOVE_CAST(function)(f), allocator_); + } + + void post(ASIO_MOVE_ARG(function) f) + { + executor_.post(ASIO_MOVE_CAST(function)(f), allocator_); + } + + void defer(ASIO_MOVE_ARG(function) f) + { + executor_.defer(ASIO_MOVE_CAST(function)(f), allocator_); + } + + type_id_result_type target_type() const ASIO_NOEXCEPT + { + return type_id(); + } + + void* target() ASIO_NOEXCEPT + { + return &executor_; + } + + const void* target() const ASIO_NOEXCEPT + { + return &executor_; + } + + bool equals(const impl_base* e) const ASIO_NOEXCEPT + { + if (this == e) + return true; + if (target_type() != e->target_type()) + return false; + return executor_ == *static_cast(e->target()); + } + +private: + mutable detail::atomic_count ref_count_; + Executor executor_; + Allocator allocator_; + + struct raw_mem + { + allocator_type allocator_; + impl* ptr_; + + explicit raw_mem(const Allocator& a) + : allocator_(a), + ptr_(allocator_.allocate(1)) + { + } + + ~raw_mem() + { + if (ptr_) + allocator_.deallocate(ptr_, 1); + } + + private: + // Disallow copying and assignment. + raw_mem(const raw_mem&); + raw_mem operator=(const raw_mem&); + }; +}; + +// Polymorphic allocator specialisation for system_executor. +template +class executor::impl + : public executor::impl_base +{ +public: + static impl_base* create(const system_executor&, + const Allocator& = Allocator()) + { + return &detail::global > >(); + } + + impl() + : impl_base(true) + { + } + + impl_base* clone() const ASIO_NOEXCEPT + { + return const_cast(static_cast(this)); + } + + void destroy() ASIO_NOEXCEPT + { + } + + void on_work_started() ASIO_NOEXCEPT + { + executor_.on_work_started(); + } + + void on_work_finished() ASIO_NOEXCEPT + { + executor_.on_work_finished(); + } + + execution_context& context() ASIO_NOEXCEPT + { + return executor_.context(); + } + + void dispatch(ASIO_MOVE_ARG(function) f) + { + executor_.dispatch(ASIO_MOVE_CAST(function)(f), allocator_); + } + + void post(ASIO_MOVE_ARG(function) f) + { + executor_.post(ASIO_MOVE_CAST(function)(f), allocator_); + } + + void defer(ASIO_MOVE_ARG(function) f) + { + executor_.defer(ASIO_MOVE_CAST(function)(f), allocator_); + } + + type_id_result_type target_type() const ASIO_NOEXCEPT + { + return type_id(); + } + + void* target() ASIO_NOEXCEPT + { + return &executor_; + } + + const void* target() const ASIO_NOEXCEPT + { + return &executor_; + } + + bool equals(const impl_base* e) const ASIO_NOEXCEPT + { + return this == e; + } + +private: + system_executor executor_; + Allocator allocator_; +}; + +template +executor::executor(Executor e) + : impl_(impl >::create(e)) +{ +} + +template +executor::executor(allocator_arg_t, const Allocator& a, Executor e) + : impl_(impl::create(e, a)) +{ +} + +template +void executor::dispatch(ASIO_MOVE_ARG(Function) f, + const Allocator& a) const +{ + impl_base* i = get_impl(); + if (i->fast_dispatch_) + system_executor().dispatch(ASIO_MOVE_CAST(Function)(f), a); + else + i->dispatch(function(ASIO_MOVE_CAST(Function)(f), a)); +} + +template +void executor::post(ASIO_MOVE_ARG(Function) f, + const Allocator& a) const +{ + get_impl()->post(function(ASIO_MOVE_CAST(Function)(f), a)); +} + +template +void executor::defer(ASIO_MOVE_ARG(Function) f, + const Allocator& a) const +{ + get_impl()->defer(function(ASIO_MOVE_CAST(Function)(f), a)); +} + +template +Executor* executor::target() ASIO_NOEXCEPT +{ + return impl_ && impl_->target_type() == type_id() + ? static_cast(impl_->target()) : 0; +} + +template +const Executor* executor::target() const ASIO_NOEXCEPT +{ + return impl_ && impl_->target_type() == type_id() + ? static_cast(impl_->target()) : 0; +} + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/impl/executor.ipp b/subprojects/asio/include/asio/impl/executor.ipp new file mode 100644 index 000000000..4bd0dc5c8 --- /dev/null +++ b/subprojects/asio/include/asio/impl/executor.ipp @@ -0,0 +1,38 @@ +// +// impl/executor.ipp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_EXECUTOR_IPP +#define ASIO_IMPL_EXECUTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +bad_executor::bad_executor() ASIO_NOEXCEPT +{ +} + +const char* bad_executor::what() const ASIO_NOEXCEPT_OR_NOTHROW +{ + return "bad executor"; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_EXECUTOR_IPP diff --git a/subprojects/asio/include/asio/impl/handler_alloc_hook.ipp b/subprojects/asio/include/asio/impl/handler_alloc_hook.ipp new file mode 100644 index 000000000..909ddace8 --- /dev/null +++ b/subprojects/asio/include/asio/impl/handler_alloc_hook.ipp @@ -0,0 +1,52 @@ +// +// impl/handler_alloc_hook.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_HANDLER_ALLOC_HOOK_IPP +#define ASIO_IMPL_HANDLER_ALLOC_HOOK_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/thread_context.hpp" +#include "asio/detail/thread_info_base.hpp" +#include "asio/handler_alloc_hook.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +void* asio_handler_allocate(std::size_t size, ...) +{ +#if !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + return detail::thread_info_base::allocate( + detail::thread_context::thread_call_stack::top(), size); +#else // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + return ::operator new(size); +#endif // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) +} + +void asio_handler_deallocate(void* pointer, std::size_t size, ...) +{ +#if !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + detail::thread_info_base::deallocate( + detail::thread_context::thread_call_stack::top(), pointer, size); +#else // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + (void)size; + ::operator delete(pointer); +#endif // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_HANDLER_ALLOC_HOOK_IPP diff --git a/subprojects/asio/include/asio/impl/io_context.hpp b/subprojects/asio/include/asio/impl/io_context.hpp new file mode 100644 index 000000000..5b6300f84 --- /dev/null +++ b/subprojects/asio/include/asio/impl/io_context.hpp @@ -0,0 +1,343 @@ +// +// impl/io_context.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_IO_CONTEXT_HPP +#define ASIO_IMPL_IO_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/executor_op.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/detail/service_registry.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +inline Service& use_service(io_context& ioc) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + (void)static_cast(&Service::id); + + return ioc.service_registry_->template use_service(ioc); +} + +template <> +inline detail::io_context_impl& use_service( + io_context& ioc) +{ + return ioc.impl_; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else +# include "asio/detail/scheduler.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +inline io_context::executor_type +io_context::get_executor() ASIO_NOEXCEPT +{ + return executor_type(*this); +} + +#if defined(ASIO_HAS_CHRONO) + +template +std::size_t io_context::run_for( + const chrono::duration& rel_time) +{ + return this->run_until(chrono::steady_clock::now() + rel_time); +} + +template +std::size_t io_context::run_until( + const chrono::time_point& abs_time) +{ + std::size_t n = 0; + while (this->run_one_until(abs_time)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +template +std::size_t io_context::run_one_for( + const chrono::duration& rel_time) +{ + return this->run_one_until(chrono::steady_clock::now() + rel_time); +} + +template +std::size_t io_context::run_one_until( + const chrono::time_point& abs_time) +{ + typename Clock::time_point now = Clock::now(); + while (now < abs_time) + { + typename Clock::duration rel_time = abs_time - now; + if (rel_time > chrono::seconds(1)) + rel_time = chrono::seconds(1); + + asio::error_code ec; + std::size_t s = impl_.wait_one( + static_cast(chrono::duration_cast< + chrono::microseconds>(rel_time).count()), ec); + asio::detail::throw_error(ec); + + if (s || impl_.stopped()) + return s; + + now = Clock::now(); + } + + return 0; +} + +#endif // defined(ASIO_HAS_CHRONO) + +#if !defined(ASIO_NO_DEPRECATED) + +inline void io_context::reset() +{ + restart(); +} + +template +ASIO_INITFN_RESULT_TYPE(LegacyCompletionHandler, void ()) +io_context::dispatch(ASIO_MOVE_ARG(LegacyCompletionHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a LegacyCompletionHandler. + ASIO_LEGACY_COMPLETION_HANDLER_CHECK( + LegacyCompletionHandler, handler) type_check; + + async_completion init(handler); + + if (impl_.can_dispatch()) + { + detail::fenced_block b(detail::fenced_block::full); + asio_handler_invoke_helpers::invoke( + init.completion_handler, init.completion_handler); + } + else + { + // Allocate and construct an operation to wrap the handler. + typedef detail::completion_handler< + typename handler_type::type> op; + typename op::ptr p = { detail::addressof(init.completion_handler), + op::ptr::allocate(init.completion_handler), 0 }; + p.p = new (p.v) op(init.completion_handler); + + ASIO_HANDLER_CREATION((*this, *p.p, + "io_context", this, 0, "dispatch")); + + impl_.do_dispatch(p.p); + p.v = p.p = 0; + } + + return init.result.get(); +} + +template +ASIO_INITFN_RESULT_TYPE(LegacyCompletionHandler, void ()) +io_context::post(ASIO_MOVE_ARG(LegacyCompletionHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a LegacyCompletionHandler. + ASIO_LEGACY_COMPLETION_HANDLER_CHECK( + LegacyCompletionHandler, handler) type_check; + + async_completion init(handler); + + bool is_continuation = + asio_handler_cont_helpers::is_continuation(init.completion_handler); + + // Allocate and construct an operation to wrap the handler. + typedef detail::completion_handler< + typename handler_type::type> op; + typename op::ptr p = { detail::addressof(init.completion_handler), + op::ptr::allocate(init.completion_handler), 0 }; + p.p = new (p.v) op(init.completion_handler); + + ASIO_HANDLER_CREATION((*this, *p.p, + "io_context", this, 0, "post")); + + impl_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + + return init.result.get(); +} + +template +#if defined(GENERATING_DOCUMENTATION) +unspecified +#else +inline detail::wrapped_handler +#endif +io_context::wrap(Handler handler) +{ + return detail::wrapped_handler(*this, handler); +} + +#endif // !defined(ASIO_NO_DEPRECATED) + +inline io_context& +io_context::executor_type::context() const ASIO_NOEXCEPT +{ + return io_context_; +} + +inline void +io_context::executor_type::on_work_started() const ASIO_NOEXCEPT +{ + io_context_.impl_.work_started(); +} + +inline void +io_context::executor_type::on_work_finished() const ASIO_NOEXCEPT +{ + io_context_.impl_.work_finished(); +} + +template +void io_context::executor_type::dispatch( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + // Invoke immediately if we are already inside the thread pool. + if (io_context_.impl_.can_dispatch()) + { + // Make a local, non-const copy of the function. + function_type tmp(ASIO_MOVE_CAST(Function)(f)); + + detail::fenced_block b(detail::fenced_block::full); + asio_handler_invoke_helpers::invoke(tmp, tmp); + return; + } + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((this->context(), *p.p, + "io_context", &this->context(), 0, "dispatch")); + + io_context_.impl_.post_immediate_completion(p.p, false); + p.v = p.p = 0; +} + +template +void io_context::executor_type::post( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((this->context(), *p.p, + "io_context", &this->context(), 0, "post")); + + io_context_.impl_.post_immediate_completion(p.p, false); + p.v = p.p = 0; +} + +template +void io_context::executor_type::defer( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((this->context(), *p.p, + "io_context", &this->context(), 0, "defer")); + + io_context_.impl_.post_immediate_completion(p.p, true); + p.v = p.p = 0; +} + +inline bool +io_context::executor_type::running_in_this_thread() const ASIO_NOEXCEPT +{ + return io_context_.impl_.can_dispatch(); +} + +#if !defined(ASIO_NO_DEPRECATED) +inline io_context::work::work(asio::io_context& io_context) + : io_context_impl_(io_context.impl_) +{ + io_context_impl_.work_started(); +} + +inline io_context::work::work(const work& other) + : io_context_impl_(other.io_context_impl_) +{ + io_context_impl_.work_started(); +} + +inline io_context::work::~work() +{ + io_context_impl_.work_finished(); +} + +inline asio::io_context& io_context::work::get_io_context() +{ + return static_cast(io_context_impl_.context()); +} + +inline asio::io_context& io_context::work::get_io_service() +{ + return static_cast(io_context_impl_.context()); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +inline asio::io_context& io_context::service::get_io_context() +{ + return static_cast(context()); +} + +#if !defined(ASIO_NO_DEPRECATED) +inline asio::io_context& io_context::service::get_io_service() +{ + return static_cast(context()); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_IO_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/impl/io_context.ipp b/subprojects/asio/include/asio/impl/io_context.ipp new file mode 100644 index 000000000..7eb467d26 --- /dev/null +++ b/subprojects/asio/include/asio/impl/io_context.ipp @@ -0,0 +1,174 @@ +// +// impl/io_context.ipp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_IO_CONTEXT_IPP +#define ASIO_IMPL_IO_CONTEXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/concurrency_hint.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/service_registry.hpp" +#include "asio/detail/throw_error.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else +# include "asio/detail/scheduler.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +io_context::io_context() + : impl_(add_impl(new impl_type(*this, ASIO_CONCURRENCY_HINT_DEFAULT))) +{ +} + +io_context::io_context(int concurrency_hint) + : impl_(add_impl(new impl_type(*this, concurrency_hint == 1 + ? ASIO_CONCURRENCY_HINT_1 : concurrency_hint))) +{ +} + +io_context::impl_type& io_context::add_impl(io_context::impl_type* impl) +{ + asio::detail::scoped_ptr scoped_impl(impl); + asio::add_service(*this, scoped_impl.get()); + return *scoped_impl.release(); +} + +io_context::~io_context() +{ +} + +io_context::count_type io_context::run() +{ + asio::error_code ec; + count_type s = impl_.run(ec); + asio::detail::throw_error(ec); + return s; +} + +#if !defined(ASIO_NO_DEPRECATED) +io_context::count_type io_context::run(asio::error_code& ec) +{ + return impl_.run(ec); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +io_context::count_type io_context::run_one() +{ + asio::error_code ec; + count_type s = impl_.run_one(ec); + asio::detail::throw_error(ec); + return s; +} + +#if !defined(ASIO_NO_DEPRECATED) +io_context::count_type io_context::run_one(asio::error_code& ec) +{ + return impl_.run_one(ec); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +io_context::count_type io_context::poll() +{ + asio::error_code ec; + count_type s = impl_.poll(ec); + asio::detail::throw_error(ec); + return s; +} + +#if !defined(ASIO_NO_DEPRECATED) +io_context::count_type io_context::poll(asio::error_code& ec) +{ + return impl_.poll(ec); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +io_context::count_type io_context::poll_one() +{ + asio::error_code ec; + count_type s = impl_.poll_one(ec); + asio::detail::throw_error(ec); + return s; +} + +#if !defined(ASIO_NO_DEPRECATED) +io_context::count_type io_context::poll_one(asio::error_code& ec) +{ + return impl_.poll_one(ec); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +void io_context::stop() +{ + impl_.stop(); +} + +bool io_context::stopped() const +{ + return impl_.stopped(); +} + +void io_context::restart() +{ + impl_.restart(); +} + +io_context::service::service(asio::io_context& owner) + : execution_context::service(owner) +{ +} + +io_context::service::~service() +{ +} + +void io_context::service::shutdown() +{ +#if !defined(ASIO_NO_DEPRECATED) + shutdown_service(); +#endif // !defined(ASIO_NO_DEPRECATED) +} + +#if !defined(ASIO_NO_DEPRECATED) +void io_context::service::shutdown_service() +{ +} +#endif // !defined(ASIO_NO_DEPRECATED) + +void io_context::service::notify_fork(io_context::fork_event ev) +{ +#if !defined(ASIO_NO_DEPRECATED) + fork_service(ev); +#else // !defined(ASIO_NO_DEPRECATED) + (void)ev; +#endif // !defined(ASIO_NO_DEPRECATED) +} + +#if !defined(ASIO_NO_DEPRECATED) +void io_context::service::fork_service(io_context::fork_event) +{ +} +#endif // !defined(ASIO_NO_DEPRECATED) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_IO_CONTEXT_IPP diff --git a/subprojects/asio/include/asio/impl/post.hpp b/subprojects/asio/include/asio/impl/post.hpp new file mode 100644 index 000000000..55389532b --- /dev/null +++ b/subprojects/asio/include/asio/impl/post.hpp @@ -0,0 +1,77 @@ +// +// impl/post.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_POST_HPP +#define ASIO_IMPL_POST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/work_dispatcher.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post( + ASIO_MOVE_ARG(CompletionToken) token) +{ + typedef ASIO_HANDLER_TYPE(CompletionToken, void()) handler; + + async_completion init(token); + + typename associated_executor::type ex( + (get_associated_executor)(init.completion_handler)); + + typename associated_allocator::type alloc( + (get_associated_allocator)(init.completion_handler)); + + ex.post(ASIO_MOVE_CAST(handler)(init.completion_handler), alloc); + + return init.result.get(); +} + +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post( + const Executor& ex, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type*) +{ + typedef ASIO_HANDLER_TYPE(CompletionToken, void()) handler; + + async_completion init(token); + + typename associated_allocator::type alloc( + (get_associated_allocator)(init.completion_handler)); + + ex.post(detail::work_dispatcher(init.completion_handler), alloc); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post( + ExecutionContext& ctx, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type*) +{ + return (post)(ctx.get_executor(), + ASIO_MOVE_CAST(CompletionToken)(token)); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_POST_HPP diff --git a/subprojects/asio/include/asio/impl/read.hpp b/subprojects/asio/include/asio/impl/read.hpp new file mode 100644 index 000000000..603a7a9b1 --- /dev/null +++ b/subprojects/asio/include/asio/impl/read.hpp @@ -0,0 +1,715 @@ +// +// impl/read.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_READ_HPP +#define ASIO_IMPL_READ_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/dependent_type.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + template + std::size_t read_buffer_sequence(SyncReadStream& s, + const MutableBufferSequence& buffers, const MutableBufferIterator&, + CompletionCondition completion_condition, asio::error_code& ec) + { + ec = asio::error_code(); + asio::detail::consuming_buffers tmp(buffers); + while (!tmp.empty()) + { + if (std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, tmp.total_consumed()))) + tmp.consume(s.read_some(tmp.prepare(max_size), ec)); + else + break; + } + return tmp.total_consumed();; + } +} // namespace detail + +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_mutable_buffer_sequence::value + >::type*) +{ + return detail::read_buffer_sequence(s, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, ec); +} + +template +inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + typename enable_if< + is_mutable_buffer_sequence::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec); + asio::detail::throw_error(ec, "read"); + return bytes_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + asio::error_code& ec, + typename enable_if< + is_mutable_buffer_sequence::value + >::type*) +{ + return read(s, buffers, transfer_all(), ec); +} + +template +inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, + typename enable_if< + is_mutable_buffer_sequence::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, buffers, completion_condition, ec); + asio::detail::throw_error(ec, "read"); + return bytes_transferred; +} + +template +std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + typename decay::type b( + ASIO_MOVE_CAST(DynamicBuffer)(buffers)); + + ec = asio::error_code(); + std::size_t total_transferred = 0; + std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + std::size_t bytes_available = std::min( + std::max(512, b.capacity() - b.size()), + std::min(max_size, b.max_size() - b.size())); + while (bytes_available > 0) + { + std::size_t bytes_transferred = s.read_some(b.prepare(bytes_available), ec); + b.commit(bytes_transferred); + total_transferred += bytes_transferred; + max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + bytes_available = std::min( + std::max(512, b.capacity() - b.size()), + std::min(max_size, b.max_size() - b.size())); + } + return total_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), transfer_all(), ec); + asio::detail::throw_error(ec, "read"); + return bytes_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + return read(s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + transfer_all(), ec); +} + +template +inline std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), + completion_condition, ec); + asio::detail::throw_error(ec, "read"); + return bytes_transferred; +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + return read(s, basic_streambuf_ref(b), completion_condition, ec); +} + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b) +{ + return read(s, basic_streambuf_ref(b)); +} + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b, + asio::error_code& ec) +{ + return read(s, basic_streambuf_ref(b), ec); +} + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + return read(s, basic_streambuf_ref(b), completion_condition); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +namespace detail +{ + template + class read_op + : detail::base_from_completion_cond + { + public: + read_op(AsyncReadStream& stream, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler& handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffers_(buffers), + start_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_op(const read_op& other) + : detail::base_from_completion_cond(other), + stream_(other.stream_), + buffers_(other.buffers_), + start_(other.start_), + handler_(other.handler_) + { + } + + read_op(read_op&& other) + : detail::base_from_completion_cond(other), + stream_(other.stream_), + buffers_(other.buffers_), + start_(other.start_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size; + switch (start_ = start) + { + case 1: + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + do + { + stream_.async_read_some(buffers_.prepare(max_size), + ASIO_MOVE_CAST(read_op)(*this)); + return; default: + buffers_.consume(bytes_transferred); + if ((!ec && bytes_transferred == 0) || buffers_.empty()) + break; + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + } while (max_size > 0); + + handler_(ec, buffers_.total_consumed()); + } + } + + //private: + AsyncReadStream& stream_; + asio::detail::consuming_buffers buffers_; + int start_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void start_read_buffer_sequence_op(AsyncReadStream& stream, + const MutableBufferSequence& buffers, const MutableBufferIterator&, + CompletionCondition completion_condition, ReadHandler& handler) + { + detail::read_op( + stream, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_mutable_buffer_sequence::value + >::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::start_read_buffer_sequence_op(s, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, + init.completion_handler); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_mutable_buffer_sequence::value + >::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::start_read_buffer_sequence_op(s, buffers, + asio::buffer_sequence_begin(buffers), transfer_all(), + init.completion_handler); + + return init.result.get(); +} + +namespace detail +{ + template + class read_dynbuf_op + : detail::base_from_completion_cond + { + public: + template + read_dynbuf_op(AsyncReadStream& stream, + ASIO_MOVE_ARG(BufferSequence) buffers, + CompletionCondition completion_condition, ReadHandler& handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)), + start_(0), + total_transferred_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_dynbuf_op(const read_dynbuf_op& other) + : detail::base_from_completion_cond(other), + stream_(other.stream_), + buffers_(other.buffers_), + start_(other.start_), + total_transferred_(other.total_transferred_), + handler_(other.handler_) + { + } + + read_dynbuf_op(read_dynbuf_op&& other) + : detail::base_from_completion_cond(other), + stream_(other.stream_), + buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)), + start_(other.start_), + total_transferred_(other.total_transferred_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size, bytes_available; + switch (start_ = start) + { + case 1: + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = std::min( + std::max(512, + buffers_.capacity() - buffers_.size()), + std::min(max_size, + buffers_.max_size() - buffers_.size())); + for (;;) + { + stream_.async_read_some(buffers_.prepare(bytes_available), + ASIO_MOVE_CAST(read_dynbuf_op)(*this)); + return; default: + total_transferred_ += bytes_transferred; + buffers_.commit(bytes_transferred); + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = std::min( + std::max(512, + buffers_.capacity() - buffers_.size()), + std::min(max_size, + buffers_.max_size() - buffers_.size())); + if ((!ec && bytes_transferred == 0) || bytes_available == 0) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncReadStream& stream_; + DynamicBuffer buffers_; + int start_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_dynbuf_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_dynbuf_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_dynbuf_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_dynbuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_dynbuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_dynbuf_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_dynbuf_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_dynbuf_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_dynbuf_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + return async_read(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), + transfer_all(), ASIO_MOVE_CAST(ReadHandler)(handler)); +} + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::read_dynbuf_op::type, + CompletionCondition, ASIO_HANDLER_TYPE( + ReadHandler, void (asio::error_code, std::size_t))>( + s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + completion_condition, init.completion_handler)( + asio::error_code(), 0, 1); + + return init.result.get(); +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, basic_streambuf& b, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + return async_read(s, basic_streambuf_ref(b), + ASIO_MOVE_CAST(ReadHandler)(handler)); +} + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, basic_streambuf& b, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + return async_read(s, basic_streambuf_ref(b), + completion_condition, ASIO_MOVE_CAST(ReadHandler)(handler)); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_READ_HPP diff --git a/subprojects/asio/include/asio/impl/read_at.hpp b/subprojects/asio/include/asio/impl/read_at.hpp new file mode 100644 index 000000000..d736d4d31 --- /dev/null +++ b/subprojects/asio/include/asio/impl/read_at.hpp @@ -0,0 +1,640 @@ +// +// impl/read_at.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_READ_AT_HPP +#define ASIO_IMPL_READ_AT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/dependent_type.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + template + std::size_t read_at_buffer_sequence(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + const MutableBufferIterator&, CompletionCondition completion_condition, + asio::error_code& ec) + { + ec = asio::error_code(); + asio::detail::consuming_buffers tmp(buffers); + while (!tmp.empty()) + { + if (std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, tmp.total_consumed()))) + { + tmp.consume(d.read_some_at(offset + tmp.total_consumed(), + tmp.prepare(max_size), ec)); + } + else + break; + } + return tmp.total_consumed();; + } +} // namespace detail + +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + return detail::read_at_buffer_sequence(d, offset, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, ec); +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, buffers, transfer_all(), ec); + asio::detail::throw_error(ec, "read_at"); + return bytes_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + asio::error_code& ec) +{ + return read_at(d, offset, buffers, transfer_all(), ec); +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, buffers, completion_condition, ec); + asio::detail::throw_error(ec, "read_at"); + return bytes_transferred; +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + ec = asio::error_code(); + std::size_t total_transferred = 0; + std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + std::size_t bytes_available = read_size_helper(b, max_size); + while (bytes_available > 0) + { + std::size_t bytes_transferred = d.read_some_at( + offset + total_transferred, b.prepare(bytes_available), ec); + b.commit(bytes_transferred); + total_transferred += bytes_transferred; + max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + bytes_available = read_size_helper(b, max_size); + } + return total_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, b, transfer_all(), ec); + asio::detail::throw_error(ec, "read_at"); + return bytes_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, asio::basic_streambuf& b, + asio::error_code& ec) +{ + return read_at(d, offset, b, transfer_all(), ec); +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, b, completion_condition, ec); + asio::detail::throw_error(ec, "read_at"); + return bytes_transferred; +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +namespace detail +{ + template + class read_at_op + : detail::base_from_completion_cond + { + public: + read_at_op(AsyncRandomAccessReadDevice& device, + uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler& handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + buffers_(buffers), + start_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_at_op(const read_at_op& other) + : detail::base_from_completion_cond(other), + device_(other.device_), + offset_(other.offset_), + buffers_(other.buffers_), + start_(other.start_), + handler_(other.handler_) + { + } + + read_at_op(read_at_op&& other) + : detail::base_from_completion_cond(other), + device_(other.device_), + offset_(other.offset_), + buffers_(other.buffers_), + start_(other.start_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size; + switch (start_ = start) + { + case 1: + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + do + { + device_.async_read_some_at( + offset_ + buffers_.total_consumed(), buffers_.prepare(max_size), + ASIO_MOVE_CAST(read_at_op)(*this)); + return; default: + buffers_.consume(bytes_transferred); + if ((!ec && bytes_transferred == 0) || buffers_.empty()) + break; + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + } while (max_size > 0); + + handler_(ec, buffers_.total_consumed()); + } + } + + //private: + AsyncRandomAccessReadDevice& device_; + uint64_t offset_; + asio::detail::consuming_buffers buffers_; + int start_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_at_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_at_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_at_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_at_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_at_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void start_read_at_buffer_sequence_op(AsyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + const MutableBufferIterator&, CompletionCondition completion_condition, + ReadHandler& handler) + { + detail::read_at_op( + d, offset, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_at_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_at_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_at_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_at_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::start_read_at_buffer_sequence_op(d, offset, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, + init.completion_handler); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::start_read_at_buffer_sequence_op(d, offset, buffers, + asio::buffer_sequence_begin(buffers), transfer_all(), + init.completion_handler); + + return init.result.get(); +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +namespace detail +{ + template + class read_at_streambuf_op + : detail::base_from_completion_cond + { + public: + read_at_streambuf_op(AsyncRandomAccessReadDevice& device, + uint64_t offset, basic_streambuf& streambuf, + CompletionCondition completion_condition, ReadHandler& handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + streambuf_(streambuf), + start_(0), + total_transferred_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_at_streambuf_op(const read_at_streambuf_op& other) + : detail::base_from_completion_cond(other), + device_(other.device_), + offset_(other.offset_), + streambuf_(other.streambuf_), + start_(other.start_), + total_transferred_(other.total_transferred_), + handler_(other.handler_) + { + } + + read_at_streambuf_op(read_at_streambuf_op&& other) + : detail::base_from_completion_cond(other), + device_(other.device_), + offset_(other.offset_), + streambuf_(other.streambuf_), + start_(other.start_), + total_transferred_(other.total_transferred_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size, bytes_available; + switch (start_ = start) + { + case 1: + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = read_size_helper(streambuf_, max_size); + for (;;) + { + device_.async_read_some_at(offset_ + total_transferred_, + streambuf_.prepare(bytes_available), + ASIO_MOVE_CAST(read_at_streambuf_op)(*this)); + return; default: + total_transferred_ += bytes_transferred; + streambuf_.commit(bytes_transferred); + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = read_size_helper(streambuf_, max_size); + if ((!ec && bytes_transferred == 0) || bytes_available == 0) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncRandomAccessReadDevice& device_; + uint64_t offset_; + asio::basic_streambuf& streambuf_; + int start_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_at_streambuf_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_at_streambuf_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_at_streambuf_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_at_streambuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_at_streambuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_at_streambuf_op, + Allocator1> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_at_streambuf_op& h, + const Allocator1& a = Allocator1()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_at_streambuf_op, + Executor1> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_at_streambuf_op& h, + const Executor1& ex = Executor1()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, + uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::read_at_streambuf_op( + d, offset, b, completion_condition, init.completion_handler)( + asio::error_code(), 0, 1); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, + uint64_t offset, asio::basic_streambuf& b, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::read_at_streambuf_op( + d, offset, b, transfer_all(), init.completion_handler)( + asio::error_code(), 0, 1); + + return init.result.get(); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_READ_AT_HPP diff --git a/subprojects/asio/include/asio/impl/read_until.hpp b/subprojects/asio/include/asio/impl/read_until.hpp new file mode 100644 index 000000000..1a093cf03 --- /dev/null +++ b/subprojects/asio/include/asio/impl/read_until.hpp @@ -0,0 +1,1502 @@ +// +// impl/read_until.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_READ_UNTIL_HPP +#define ASIO_IMPL_READ_UNTIL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/buffer.hpp" +#include "asio/buffers_iterator.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +inline std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, char delim) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), delim, ec); + asio::detail::throw_error(ec, "read_until"); + return bytes_transferred; +} + +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + char delim, asio::error_code& ec) +{ + typename decay::type b( + ASIO_MOVE_CAST(DynamicBuffer)(buffers)); + + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = b.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position; + iterator end = iterator::end(data_buffers); + + // Look for a match. + iterator iter = std::find(start_pos, end, delim); + if (iter != end) + { + // Found a match. We're done. + ec = asio::error_code(); + return iter - begin + 1; + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = std::min( + std::max(512, b.capacity() - b.size()), + std::min(65536, b.max_size() - b.size())); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +template +inline std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_STRING_VIEW_PARAM delim) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), delim, ec); + asio::detail::throw_error(ec, "read_until"); + return bytes_transferred; +} + +namespace detail +{ + // Algorithm that finds a subsequence of equal values in a sequence. Returns + // (iterator,true) if a full match was found, in which case the iterator + // points to the beginning of the match. Returns (iterator,false) if a + // partial match was found at the end of the first sequence, in which case + // the iterator points to the beginning of the partial match. Returns + // (last1,false) if no full or partial match was found. + template + std::pair partial_search( + Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) + { + for (Iterator1 iter1 = first1; iter1 != last1; ++iter1) + { + Iterator1 test_iter1 = iter1; + Iterator2 test_iter2 = first2; + for (;; ++test_iter1, ++test_iter2) + { + if (test_iter2 == last2) + return std::make_pair(iter1, true); + if (test_iter1 == last1) + { + if (test_iter2 != first2) + return std::make_pair(iter1, false); + else + break; + } + if (*test_iter1 != *test_iter2) + break; + } + } + return std::make_pair(last1, false); + } +} // namespace detail + +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_STRING_VIEW_PARAM delim, asio::error_code& ec) +{ + typename decay::type b( + ASIO_MOVE_CAST(DynamicBuffer)(buffers)); + + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = b.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position; + iterator end = iterator::end(data_buffers); + + // Look for a match. + std::pair result = detail::partial_search( + start_pos, end, delim.begin(), delim.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + ec = asio::error_code(); + return result.first - begin + delim.length(); + } + else + { + // Partial match. Next search needs to start from beginning of match. + search_position = result.first - begin; + } + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = std::min( + std::max(512, b.capacity() - b.size()), + std::min(65536, b.max_size() - b.size())); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if defined(ASIO_HAS_BOOST_REGEX) + +template +inline std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + const boost::regex& expr) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), expr, ec); + asio::detail::throw_error(ec, "read_until"); + return bytes_transferred; +} + +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + const boost::regex& expr, asio::error_code& ec) +{ + typename decay::type b( + ASIO_MOVE_CAST(DynamicBuffer)(buffers)); + + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = b.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position; + iterator end = iterator::end(data_buffers); + + // Look for a match. + boost::match_results >::allocator_type> + match_results; + if (regex_search(start_pos, end, match_results, expr, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + ec = asio::error_code(); + return match_results[0].second - begin; + } + else + { + // Partial match. Next search needs to start from beginning of match. + search_position = match_results[0].first - begin; + } + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = std::min( + std::max(512, b.capacity() - b.size()), + std::min(65536, b.max_size() - b.size())); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +#endif // defined(ASIO_HAS_BOOST_REGEX) + +template +inline std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + MatchCondition match_condition, + typename enable_if::value>::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), + match_condition, ec); + asio::detail::throw_error(ec, "read_until"); + return bytes_transferred; +} + +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + MatchCondition match_condition, asio::error_code& ec, + typename enable_if::value>::type*) +{ + typename decay::type b( + ASIO_MOVE_CAST(DynamicBuffer)(buffers)); + + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = b.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position; + iterator end = iterator::end(data_buffers); + + // Look for a match. + std::pair result = match_condition(start_pos, end); + if (result.second) + { + // Full match. We're done. + ec = asio::error_code(); + return result.first - begin; + } + else if (result.first != end) + { + // Partial match. Next search needs to start from beginning of match. + search_position = result.first - begin; + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = std::min( + std::max(512, b.capacity() - b.size()), + std::min(65536, b.max_size() - b.size())); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +#if !defined(ASIO_NO_IOSTREAM) + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim) +{ + return read_until(s, basic_streambuf_ref(b), delim); +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim, + asio::error_code& ec) +{ + return read_until(s, basic_streambuf_ref(b), delim, ec); +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + ASIO_STRING_VIEW_PARAM delim) +{ + return read_until(s, basic_streambuf_ref(b), delim); +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + ASIO_STRING_VIEW_PARAM delim, asio::error_code& ec) +{ + return read_until(s, basic_streambuf_ref(b), delim, ec); +} + +#if defined(ASIO_HAS_BOOST_REGEX) + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr) +{ + return read_until(s, basic_streambuf_ref(b), expr); +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + asio::error_code& ec) +{ + return read_until(s, basic_streambuf_ref(b), expr, ec); +} + +#endif // defined(ASIO_HAS_BOOST_REGEX) + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, MatchCondition match_condition, + typename enable_if::value>::type*) +{ + return read_until(s, basic_streambuf_ref(b), match_condition); +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, asio::error_code& ec, + typename enable_if::value>::type*) +{ + return read_until(s, basic_streambuf_ref(b), match_condition, ec); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +namespace detail +{ + template + class read_until_delim_op + { + public: + template + read_until_delim_op(AsyncReadStream& stream, + ASIO_MOVE_ARG(BufferSequence) buffers, + char delim, ReadHandler& handler) + : stream_(stream), + buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)), + delim_(delim), + start_(0), + search_position_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_until_delim_op(const read_until_delim_op& other) + : stream_(other.stream_), + buffers_(other.buffers_), + delim_(other.delim_), + start_(other.start_), + search_position_(other.search_position_), + handler_(other.handler_) + { + } + + read_until_delim_op(read_until_delim_op&& other) + : stream_(other.stream_), + buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)), + delim_(other.delim_), + start_(other.start_), + search_position_(other.search_position_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start_ = start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type + buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = buffers_.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position_; + iterator end = iterator::end(data_buffers); + + // Look for a match. + iterator iter = std::find(start_pos, end, delim_); + if (iter != end) + { + // Found a match. We're done. + search_position_ = iter - begin + 1; + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (buffers_.size() == buffers_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + // Next search can start with the new data. + search_position_ = end - begin; + bytes_to_read = std::min( + std::max(512, + buffers_.capacity() - buffers_.size()), + std::min(65536, + buffers_.max_size() - buffers_.size())); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(buffers_.prepare(bytes_to_read), + ASIO_MOVE_CAST(read_until_delim_op)(*this)); + return; default: + buffers_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + DynamicBuffer buffers_; + char delim_; + int start_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_until_delim_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_until_delim_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_until_delim_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_until_delim_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_until_delim_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_until_delim_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + char delim, ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::read_until_delim_op::type, + ASIO_HANDLER_TYPE(ReadHandler, + void (asio::error_code, std::size_t))>( + s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + delim, init.completion_handler)(asio::error_code(), 0, 1); + + return init.result.get(); +} + +namespace detail +{ + template + class read_until_delim_string_op + { + public: + template + read_until_delim_string_op(AsyncReadStream& stream, + ASIO_MOVE_ARG(BufferSequence) buffers, + const std::string& delim, ReadHandler& handler) + : stream_(stream), + buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)), + delim_(delim), + start_(0), + search_position_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_until_delim_string_op(const read_until_delim_string_op& other) + : stream_(other.stream_), + buffers_(other.buffers_), + delim_(other.delim_), + start_(other.start_), + search_position_(other.search_position_), + handler_(other.handler_) + { + } + + read_until_delim_string_op(read_until_delim_string_op&& other) + : stream_(other.stream_), + buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)), + delim_(ASIO_MOVE_CAST(std::string)(other.delim_)), + start_(other.start_), + search_position_(other.search_position_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start_ = start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type + buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = buffers_.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position_; + iterator end = iterator::end(data_buffers); + + // Look for a match. + std::pair result = detail::partial_search( + start_pos, end, delim_.begin(), delim_.end()); + if (result.first != end && result.second) + { + // Full match. We're done. + search_position_ = result.first - begin + delim_.length(); + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (buffers_.size() == buffers_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + if (result.first != end) + { + // Partial match. Next search needs to start from beginning of + // match. + search_position_ = result.first - begin; + } + else + { + // Next search can start with the new data. + search_position_ = end - begin; + } + + bytes_to_read = std::min( + std::max(512, + buffers_.capacity() - buffers_.size()), + std::min(65536, + buffers_.max_size() - buffers_.size())); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(buffers_.prepare(bytes_to_read), + ASIO_MOVE_CAST(read_until_delim_string_op)(*this)); + return; default: + buffers_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + DynamicBuffer buffers_; + std::string delim_; + int start_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_string_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_string_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_until_delim_string_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_until_delim_string_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_string_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_until_delim_string_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_until_delim_string_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_until_delim_string_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_until_delim_string_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_STRING_VIEW_PARAM delim, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::read_until_delim_string_op::type, + ASIO_HANDLER_TYPE(ReadHandler, + void (asio::error_code, std::size_t))>( + s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + static_cast(delim), + init.completion_handler)(asio::error_code(), 0, 1); + + return init.result.get(); +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if defined(ASIO_HAS_BOOST_REGEX) + +namespace detail +{ + template + class read_until_expr_op + { + public: + template + read_until_expr_op(AsyncReadStream& stream, + ASIO_MOVE_ARG(BufferSequence) buffers, + const boost::regex& expr, ReadHandler& handler) + : stream_(stream), + buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)), + expr_(expr), + start_(0), + search_position_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_until_expr_op(const read_until_expr_op& other) + : stream_(other.stream_), + buffers_(other.buffers_), + expr_(other.expr_), + start_(other.start_), + search_position_(other.search_position_), + handler_(other.handler_) + { + } + + read_until_expr_op(read_until_expr_op&& other) + : stream_(other.stream_), + buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)), + expr_(other.expr_), + start_(other.start_), + search_position_(other.search_position_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start_ = start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type + buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = buffers_.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position_; + iterator end = iterator::end(data_buffers); + + // Look for a match. + boost::match_results >::allocator_type> + match_results; + bool match = regex_search(start_pos, end, match_results, expr_, + boost::match_default | boost::match_partial); + if (match && match_results[0].matched) + { + // Full match. We're done. + search_position_ = match_results[0].second - begin; + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (buffers_.size() == buffers_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + if (match) + { + // Partial match. Next search needs to start from beginning of + // match. + search_position_ = match_results[0].first - begin; + } + else + { + // Next search can start with the new data. + search_position_ = end - begin; + } + + bytes_to_read = std::min( + std::max(512, + buffers_.capacity() - buffers_.size()), + std::min(65536, + buffers_.max_size() - buffers_.size())); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(buffers_.prepare(bytes_to_read), + ASIO_MOVE_CAST(read_until_expr_op)(*this)); + return; default: + buffers_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + DynamicBuffer buffers_; + RegEx expr_; + int start_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_expr_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_expr_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_until_expr_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_until_expr_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_expr_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_until_expr_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_until_expr_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_until_expr_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_until_expr_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + const boost::regex& expr, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::read_until_expr_op::type, + boost::regex, ASIO_HANDLER_TYPE(ReadHandler, + void (asio::error_code, std::size_t))>( + s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + expr, init.completion_handler)(asio::error_code(), 0, 1); + + return init.result.get(); +} + +#endif // defined(ASIO_HAS_BOOST_REGEX) + +namespace detail +{ + template + class read_until_match_op + { + public: + template + read_until_match_op(AsyncReadStream& stream, + ASIO_MOVE_ARG(BufferSequence) buffers, + MatchCondition match_condition, ReadHandler& handler) + : stream_(stream), + buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)), + match_condition_(match_condition), + start_(0), + search_position_(0), + handler_(ASIO_MOVE_CAST(ReadHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + read_until_match_op(const read_until_match_op& other) + : stream_(other.stream_), + buffers_(other.buffers_), + match_condition_(other.match_condition_), + start_(other.start_), + search_position_(other.search_position_), + handler_(other.handler_) + { + } + + read_until_match_op(read_until_match_op&& other) + : stream_(other.stream_), + buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)), + match_condition_(other.match_condition_), + start_(other.start_), + search_position_(other.search_position_), + handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start_ = start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename DynamicBuffer::const_buffers_type + buffers_type; + typedef buffers_iterator iterator; + buffers_type data_buffers = buffers_.data(); + iterator begin = iterator::begin(data_buffers); + iterator start_pos = begin + search_position_; + iterator end = iterator::end(data_buffers); + + // Look for a match. + std::pair result = match_condition_(start_pos, end); + if (result.second) + { + // Full match. We're done. + search_position_ = result.first - begin; + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (buffers_.size() == buffers_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + if (result.first != end) + { + // Partial match. Next search needs to start from beginning of + // match. + search_position_ = result.first - begin; + } + else + { + // Next search can start with the new data. + search_position_ = end - begin; + } + + bytes_to_read = std::min( + std::max(512, + buffers_.capacity() - buffers_.size()), + std::min(65536, + buffers_.max_size() - buffers_.size())); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(buffers_.prepare(bytes_to_read), + ASIO_MOVE_CAST(read_until_match_op)(*this)); + return; default: + buffers_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + DynamicBuffer buffers_; + MatchCondition match_condition_; + int start_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_match_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_match_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + read_until_match_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + read_until_match_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_match_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::read_until_match_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::read_until_match_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::read_until_match_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::read_until_match_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + MatchCondition match_condition, ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if::value>::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + detail::read_until_match_op::type, + MatchCondition, ASIO_HANDLER_TYPE(ReadHandler, + void (asio::error_code, std::size_t))>( + s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + match_condition, init.completion_handler)( + asio::error_code(), 0, 1); + + return init.result.get(); +} + +#if !defined(ASIO_NO_IOSTREAM) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + char delim, ASIO_MOVE_ARG(ReadHandler) handler) +{ + return async_read_until(s, basic_streambuf_ref(b), + delim, ASIO_MOVE_CAST(ReadHandler)(handler)); +} + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + ASIO_STRING_VIEW_PARAM delim, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + return async_read_until(s, basic_streambuf_ref(b), + delim, ASIO_MOVE_CAST(ReadHandler)(handler)); +} + +#if defined(ASIO_HAS_BOOST_REGEX) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + ASIO_MOVE_ARG(ReadHandler) handler) +{ + return async_read_until(s, basic_streambuf_ref(b), + expr, ASIO_MOVE_CAST(ReadHandler)(handler)); +} + +#endif // defined(ASIO_HAS_BOOST_REGEX) + +template +inline ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if::value>::type*) +{ + return async_read_until(s, basic_streambuf_ref(b), + match_condition, ASIO_MOVE_CAST(ReadHandler)(handler)); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_READ_UNTIL_HPP diff --git a/subprojects/asio/include/asio/impl/serial_port_base.hpp b/subprojects/asio/include/asio/impl/serial_port_base.hpp new file mode 100644 index 000000000..cdc201d8a --- /dev/null +++ b/subprojects/asio/include/asio/impl/serial_port_base.hpp @@ -0,0 +1,59 @@ +// +// impl/serial_port_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SERIAL_PORT_BASE_HPP +#define ASIO_IMPL_SERIAL_PORT_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +inline serial_port_base::baud_rate::baud_rate(unsigned int rate) + : value_(rate) +{ +} + +inline unsigned int serial_port_base::baud_rate::value() const +{ + return value_; +} + +inline serial_port_base::flow_control::type +serial_port_base::flow_control::value() const +{ + return value_; +} + +inline serial_port_base::parity::type serial_port_base::parity::value() const +{ + return value_; +} + +inline serial_port_base::stop_bits::type +serial_port_base::stop_bits::value() const +{ + return value_; +} + +inline unsigned int serial_port_base::character_size::value() const +{ + return value_; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_SERIAL_PORT_BASE_HPP diff --git a/subprojects/asio/include/asio/impl/serial_port_base.ipp b/subprojects/asio/include/asio/impl/serial_port_base.ipp new file mode 100644 index 000000000..5d52ed59c --- /dev/null +++ b/subprojects/asio/include/asio/impl/serial_port_base.ipp @@ -0,0 +1,554 @@ +// +// impl/serial_port_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SERIAL_PORT_BASE_IPP +#define ASIO_IMPL_SERIAL_PORT_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) + +#include +#include "asio/error.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/detail/throw_exception.hpp" + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_OPTION_STORAGE implementation_defined +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# define ASIO_OPTION_STORAGE DCB +#else +# define ASIO_OPTION_STORAGE termios +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +ASIO_SYNC_OP_VOID serial_port_base::baud_rate::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + storage.BaudRate = value_; +#else + speed_t baud; + switch (value_) + { + // Do POSIX-specified rates first. + case 0: baud = B0; break; + case 50: baud = B50; break; + case 75: baud = B75; break; + case 110: baud = B110; break; + case 134: baud = B134; break; + case 150: baud = B150; break; + case 200: baud = B200; break; + case 300: baud = B300; break; + case 600: baud = B600; break; + case 1200: baud = B1200; break; + case 1800: baud = B1800; break; + case 2400: baud = B2400; break; + case 4800: baud = B4800; break; + case 9600: baud = B9600; break; + case 19200: baud = B19200; break; + case 38400: baud = B38400; break; + // And now the extended ones conditionally. +# ifdef B7200 + case 7200: baud = B7200; break; +# endif +# ifdef B14400 + case 14400: baud = B14400; break; +# endif +# ifdef B57600 + case 57600: baud = B57600; break; +# endif +# ifdef B115200 + case 115200: baud = B115200; break; +# endif +# ifdef B230400 + case 230400: baud = B230400; break; +# endif +# ifdef B460800 + case 460800: baud = B460800; break; +# endif +# ifdef B500000 + case 500000: baud = B500000; break; +# endif +# ifdef B576000 + case 576000: baud = B576000; break; +# endif +# ifdef B921600 + case 921600: baud = B921600; break; +# endif +# ifdef B1000000 + case 1000000: baud = B1000000; break; +# endif +# ifdef B1152000 + case 1152000: baud = B1152000; break; +# endif +# ifdef B2000000 + case 2000000: baud = B2000000; break; +# endif +# ifdef B3000000 + case 3000000: baud = B3000000; break; +# endif +# ifdef B3500000 + case 3500000: baud = B3500000; break; +# endif +# ifdef B4000000 + case 4000000: baud = B4000000; break; +# endif + default: + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } +# if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE) + ::cfsetspeed(&storage, baud); +# else + ::cfsetispeed(&storage, baud); + ::cfsetospeed(&storage, baud); +# endif +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID serial_port_base::baud_rate::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + value_ = storage.BaudRate; +#else + speed_t baud = ::cfgetospeed(&storage); + switch (baud) + { + // First do those specified by POSIX. + case B0: value_ = 0; break; + case B50: value_ = 50; break; + case B75: value_ = 75; break; + case B110: value_ = 110; break; + case B134: value_ = 134; break; + case B150: value_ = 150; break; + case B200: value_ = 200; break; + case B300: value_ = 300; break; + case B600: value_ = 600; break; + case B1200: value_ = 1200; break; + case B1800: value_ = 1800; break; + case B2400: value_ = 2400; break; + case B4800: value_ = 4800; break; + case B9600: value_ = 9600; break; + case B19200: value_ = 19200; break; + case B38400: value_ = 38400; break; + // Now conditionally handle a bunch of extended rates. +# ifdef B7200 + case B7200: value_ = 7200; break; +# endif +# ifdef B14400 + case B14400: value_ = 14400; break; +# endif +# ifdef B57600 + case B57600: value_ = 57600; break; +# endif +# ifdef B115200 + case B115200: value_ = 115200; break; +# endif +# ifdef B230400 + case B230400: value_ = 230400; break; +# endif +# ifdef B460800 + case B460800: value_ = 460800; break; +# endif +# ifdef B500000 + case B500000: value_ = 500000; break; +# endif +# ifdef B576000 + case B576000: value_ = 576000; break; +# endif +# ifdef B921600 + case B921600: value_ = 921600; break; +# endif +# ifdef B1000000 + case B1000000: value_ = 1000000; break; +# endif +# ifdef B1152000 + case B1152000: value_ = 1152000; break; +# endif +# ifdef B2000000 + case B2000000: value_ = 2000000; break; +# endif +# ifdef B3000000 + case B3000000: value_ = 3000000; break; +# endif +# ifdef B3500000 + case B3500000: value_ = 3500000; break; +# endif +# ifdef B4000000 + case B4000000: value_ = 4000000; break; +# endif + default: + value_ = 0; + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +serial_port_base::flow_control::flow_control( + serial_port_base::flow_control::type t) + : value_(t) +{ + if (t != none && t != software && t != hardware) + { + std::out_of_range ex("invalid flow_control value"); + asio::detail::throw_exception(ex); + } +} + +ASIO_SYNC_OP_VOID serial_port_base::flow_control::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + storage.fOutxCtsFlow = FALSE; + storage.fOutxDsrFlow = FALSE; + storage.fTXContinueOnXoff = TRUE; + storage.fDtrControl = DTR_CONTROL_ENABLE; + storage.fDsrSensitivity = FALSE; + storage.fOutX = FALSE; + storage.fInX = FALSE; + storage.fRtsControl = RTS_CONTROL_ENABLE; + switch (value_) + { + case none: + break; + case software: + storage.fOutX = TRUE; + storage.fInX = TRUE; + break; + case hardware: + storage.fOutxCtsFlow = TRUE; + storage.fRtsControl = RTS_CONTROL_HANDSHAKE; + break; + default: + break; + } +#else + switch (value_) + { + case none: + storage.c_iflag &= ~(IXOFF | IXON); +# if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE) + storage.c_cflag &= ~CRTSCTS; +# elif defined(__QNXNTO__) + storage.c_cflag &= ~(IHFLOW | OHFLOW); +# endif + break; + case software: + storage.c_iflag |= IXOFF | IXON; +# if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE) + storage.c_cflag &= ~CRTSCTS; +# elif defined(__QNXNTO__) + storage.c_cflag &= ~(IHFLOW | OHFLOW); +# endif + break; + case hardware: +# if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE) + storage.c_iflag &= ~(IXOFF | IXON); + storage.c_cflag |= CRTSCTS; + break; +# elif defined(__QNXNTO__) + storage.c_iflag &= ~(IXOFF | IXON); + storage.c_cflag |= (IHFLOW | OHFLOW); + break; +# else + ec = asio::error::operation_not_supported; + ASIO_SYNC_OP_VOID_RETURN(ec); +# endif + default: + break; + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID serial_port_base::flow_control::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + if (storage.fOutX && storage.fInX) + { + value_ = software; + } + else if (storage.fOutxCtsFlow && storage.fRtsControl == RTS_CONTROL_HANDSHAKE) + { + value_ = hardware; + } + else + { + value_ = none; + } +#else + if (storage.c_iflag & (IXOFF | IXON)) + { + value_ = software; + } +# if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE) + else if (storage.c_cflag & CRTSCTS) + { + value_ = hardware; + } +# elif defined(__QNXNTO__) + else if (storage.c_cflag & IHFLOW && storage.c_cflag & OHFLOW) + { + value_ = hardware; + } +# endif + else + { + value_ = none; + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +serial_port_base::parity::parity(serial_port_base::parity::type t) + : value_(t) +{ + if (t != none && t != odd && t != even) + { + std::out_of_range ex("invalid parity value"); + asio::detail::throw_exception(ex); + } +} + +ASIO_SYNC_OP_VOID serial_port_base::parity::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + switch (value_) + { + case none: + storage.fParity = FALSE; + storage.Parity = NOPARITY; + break; + case odd: + storage.fParity = TRUE; + storage.Parity = ODDPARITY; + break; + case even: + storage.fParity = TRUE; + storage.Parity = EVENPARITY; + break; + default: + break; + } +#else + switch (value_) + { + case none: + storage.c_iflag |= IGNPAR; + storage.c_cflag &= ~(PARENB | PARODD); + break; + case even: + storage.c_iflag &= ~(IGNPAR | PARMRK); + storage.c_iflag |= INPCK; + storage.c_cflag |= PARENB; + storage.c_cflag &= ~PARODD; + break; + case odd: + storage.c_iflag &= ~(IGNPAR | PARMRK); + storage.c_iflag |= INPCK; + storage.c_cflag |= (PARENB | PARODD); + break; + default: + break; + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID serial_port_base::parity::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + if (storage.Parity == EVENPARITY) + { + value_ = even; + } + else if (storage.Parity == ODDPARITY) + { + value_ = odd; + } + else + { + value_ = none; + } +#else + if (storage.c_cflag & PARENB) + { + if (storage.c_cflag & PARODD) + { + value_ = odd; + } + else + { + value_ = even; + } + } + else + { + value_ = none; + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +serial_port_base::stop_bits::stop_bits( + serial_port_base::stop_bits::type t) + : value_(t) +{ + if (t != one && t != onepointfive && t != two) + { + std::out_of_range ex("invalid stop_bits value"); + asio::detail::throw_exception(ex); + } +} + +ASIO_SYNC_OP_VOID serial_port_base::stop_bits::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + switch (value_) + { + case one: + storage.StopBits = ONESTOPBIT; + break; + case onepointfive: + storage.StopBits = ONE5STOPBITS; + break; + case two: + storage.StopBits = TWOSTOPBITS; + break; + default: + break; + } +#else + switch (value_) + { + case one: + storage.c_cflag &= ~CSTOPB; + break; + case two: + storage.c_cflag |= CSTOPB; + break; + default: + ec = asio::error::operation_not_supported; + ASIO_SYNC_OP_VOID_RETURN(ec); + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID serial_port_base::stop_bits::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + if (storage.StopBits == ONESTOPBIT) + { + value_ = one; + } + else if (storage.StopBits == ONE5STOPBITS) + { + value_ = onepointfive; + } + else if (storage.StopBits == TWOSTOPBITS) + { + value_ = two; + } + else + { + value_ = one; + } +#else + value_ = (storage.c_cflag & CSTOPB) ? two : one; +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +serial_port_base::character_size::character_size(unsigned int t) + : value_(t) +{ + if (t < 5 || t > 8) + { + std::out_of_range ex("invalid character_size value"); + asio::detail::throw_exception(ex); + } +} + +ASIO_SYNC_OP_VOID serial_port_base::character_size::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + storage.ByteSize = value_; +#else + storage.c_cflag &= ~CSIZE; + switch (value_) + { + case 5: storage.c_cflag |= CS5; break; + case 6: storage.c_cflag |= CS6; break; + case 7: storage.c_cflag |= CS7; break; + case 8: storage.c_cflag |= CS8; break; + default: break; + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID serial_port_base::character_size::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + value_ = storage.ByteSize; +#else + if ((storage.c_cflag & CSIZE) == CS5) { value_ = 5; } + else if ((storage.c_cflag & CSIZE) == CS6) { value_ = 6; } + else if ((storage.c_cflag & CSIZE) == CS7) { value_ = 7; } + else if ((storage.c_cflag & CSIZE) == CS8) { value_ = 8; } + else + { + // Hmmm, use 8 for now. + value_ = 8; + } +#endif + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#undef ASIO_OPTION_STORAGE + +#endif // defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_IMPL_SERIAL_PORT_BASE_IPP diff --git a/subprojects/asio/include/asio/impl/spawn.hpp b/subprojects/asio/include/asio/impl/spawn.hpp new file mode 100644 index 000000000..5594ad93d --- /dev/null +++ b/subprojects/asio/include/asio/impl/spawn.hpp @@ -0,0 +1,535 @@ +// +// impl/spawn.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SPAWN_HPP +#define ASIO_IMPL_SPAWN_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/async_result.hpp" +#include "asio/bind_executor.hpp" +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + + template + class coro_handler + { + public: + coro_handler(basic_yield_context ctx) + : coro_(ctx.coro_.lock()), + ca_(ctx.ca_), + handler_(ctx.handler_), + ready_(0), + ec_(ctx.ec_), + value_(0) + { + } + + void operator()(T value) + { + *ec_ = asio::error_code(); + *value_ = ASIO_MOVE_CAST(T)(value); + if (--*ready_ == 0) + (*coro_)(); + } + + void operator()(asio::error_code ec, T value) + { + *ec_ = ec; + *value_ = ASIO_MOVE_CAST(T)(value); + if (--*ready_ == 0) + (*coro_)(); + } + + //private: + shared_ptr::callee_type> coro_; + typename basic_yield_context::caller_type& ca_; + Handler handler_; + atomic_count* ready_; + asio::error_code* ec_; + T* value_; + }; + + template + class coro_handler + { + public: + coro_handler(basic_yield_context ctx) + : coro_(ctx.coro_.lock()), + ca_(ctx.ca_), + handler_(ctx.handler_), + ready_(0), + ec_(ctx.ec_) + { + } + + void operator()() + { + *ec_ = asio::error_code(); + if (--*ready_ == 0) + (*coro_)(); + } + + void operator()(asio::error_code ec) + { + *ec_ = ec; + if (--*ready_ == 0) + (*coro_)(); + } + + //private: + shared_ptr::callee_type> coro_; + typename basic_yield_context::caller_type& ca_; + Handler handler_; + atomic_count* ready_; + asio::error_code* ec_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + coro_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + coro_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation(coro_handler*) + { + return true; + } + + template + inline void asio_handler_invoke(Function& function, + coro_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + coro_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + class coro_async_result + { + public: + typedef coro_handler completion_handler_type; + typedef T return_type; + + explicit coro_async_result(completion_handler_type& h) + : handler_(h), + ca_(h.ca_), + ready_(2) + { + h.ready_ = &ready_; + out_ec_ = h.ec_; + if (!out_ec_) h.ec_ = &ec_; + h.value_ = &value_; + } + + return_type get() + { + // Must not hold shared_ptr to coro while suspended. + handler_.coro_.reset(); + + if (--ready_ != 0) + ca_(); + if (!out_ec_ && ec_) throw asio::system_error(ec_); + return ASIO_MOVE_CAST(return_type)(value_); + } + + private: + completion_handler_type& handler_; + typename basic_yield_context::caller_type& ca_; + atomic_count ready_; + asio::error_code* out_ec_; + asio::error_code ec_; + return_type value_; + }; + + template + class coro_async_result + { + public: + typedef coro_handler completion_handler_type; + typedef void return_type; + + explicit coro_async_result(completion_handler_type& h) + : handler_(h), + ca_(h.ca_), + ready_(2) + { + h.ready_ = &ready_; + out_ec_ = h.ec_; + if (!out_ec_) h.ec_ = &ec_; + } + + void get() + { + // Must not hold shared_ptr to coro while suspended. + handler_.coro_.reset(); + + if (--ready_ != 0) + ca_(); + if (!out_ec_ && ec_) throw asio::system_error(ec_); + } + + private: + completion_handler_type& handler_; + typename basic_yield_context::caller_type& ca_; + atomic_count ready_; + asio::error_code* out_ec_; + asio::error_code ec_; + }; + +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +class async_result, ReturnType()> + : public detail::coro_async_result +{ +public: + explicit async_result( + typename detail::coro_async_result::completion_handler_type& h) + : detail::coro_async_result(h) + { + } +}; + +template +class async_result, ReturnType(Arg1)> + : public detail::coro_async_result::type> +{ +public: + explicit async_result( + typename detail::coro_async_result::type>::completion_handler_type& h) + : detail::coro_async_result::type>(h) + { + } +}; + +template +class async_result, + ReturnType(asio::error_code)> + : public detail::coro_async_result +{ +public: + explicit async_result( + typename detail::coro_async_result::completion_handler_type& h) + : detail::coro_async_result(h) + { + } +}; + +template +class async_result, + ReturnType(asio::error_code, Arg2)> + : public detail::coro_async_result::type> +{ +public: + explicit async_result( + typename detail::coro_async_result::type>::completion_handler_type& h) + : detail::coro_async_result::type>(h) + { + } +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template +struct handler_type, ReturnType()> +{ + typedef detail::coro_handler type; +}; + +template +struct handler_type, ReturnType(Arg1)> +{ + typedef detail::coro_handler::type> type; +}; + +template +struct handler_type, + ReturnType(asio::error_code)> +{ + typedef detail::coro_handler type; +}; + +template +struct handler_type, + ReturnType(asio::error_code, Arg2)> +{ + typedef detail::coro_handler::type> type; +}; + +template +class async_result > + : public detail::coro_async_result +{ +public: + typedef typename detail::coro_async_result::return_type type; + + explicit async_result( + typename detail::coro_async_result::completion_handler_type& h) + : detail::coro_async_result(h) + { + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::coro_handler& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const detail::coro_handler& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +namespace detail { + + template + struct spawn_data : private noncopyable + { + template + spawn_data(ASIO_MOVE_ARG(Hand) handler, + bool call_handler, ASIO_MOVE_ARG(Func) function) + : handler_(ASIO_MOVE_CAST(Hand)(handler)), + call_handler_(call_handler), + function_(ASIO_MOVE_CAST(Func)(function)) + { + } + + weak_ptr::callee_type> coro_; + Handler handler_; + bool call_handler_; + Function function_; + }; + + template + struct coro_entry_point + { + void operator()(typename basic_yield_context::caller_type& ca) + { + shared_ptr > data(data_); +#if !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2) + ca(); // Yield until coroutine pointer has been initialised. +#endif // !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2) + const basic_yield_context yield( + data->coro_, ca, data->handler_); + + (data->function_)(yield); + if (data->call_handler_) + (data->handler_)(); + } + + shared_ptr > data_; + }; + + template + struct spawn_helper + { + void operator()() + { + typedef typename basic_yield_context::callee_type callee_type; + coro_entry_point entry_point = { data_ }; + shared_ptr coro(new callee_type(entry_point, attributes_)); + data_->coro_ = coro; + (*coro)(); + } + + shared_ptr > data_; + boost::coroutines::attributes attributes_; + }; + + template + inline void asio_handler_invoke(Function& function, + spawn_helper* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->data_->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + spawn_helper* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->data_->handler_); + } + + inline void default_spawn_handler() {} + +} // namespace detail + +template +inline void spawn(ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes) +{ + typedef typename decay::type function_type; + + typename associated_executor::type ex( + (get_associated_executor)(function)); + + asio::spawn(ex, ASIO_MOVE_CAST(Function)(function), attributes); +} + +template +void spawn(ASIO_MOVE_ARG(Handler) handler, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes, + typename enable_if::type>::value && + !is_convertible::value>::type*) +{ + typedef typename decay::type handler_type; + typedef typename decay::type function_type; + + typename associated_executor::type ex( + (get_associated_executor)(handler)); + + typename associated_allocator::type a( + (get_associated_allocator)(handler)); + + detail::spawn_helper helper; + helper.data_.reset( + new detail::spawn_data( + ASIO_MOVE_CAST(Handler)(handler), true, + ASIO_MOVE_CAST(Function)(function))); + helper.attributes_ = attributes; + + ex.dispatch(helper, a); +} + +template +void spawn(basic_yield_context ctx, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes) +{ + typedef typename decay::type function_type; + + Handler handler(ctx.handler_); // Explicit copy that might be moved from. + + typename associated_executor::type ex( + (get_associated_executor)(handler)); + + typename associated_allocator::type a( + (get_associated_allocator)(handler)); + + detail::spawn_helper helper; + helper.data_.reset( + new detail::spawn_data( + ASIO_MOVE_CAST(Handler)(handler), false, + ASIO_MOVE_CAST(Function)(function))); + helper.attributes_ = attributes; + + ex.dispatch(helper, a); +} + +template +inline void spawn(const Executor& ex, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes, + typename enable_if::value>::type*) +{ + asio::spawn(asio::strand(ex), + ASIO_MOVE_CAST(Function)(function), attributes); +} + +template +inline void spawn(const strand& ex, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes) +{ + asio::spawn(asio::bind_executor( + ex, &detail::default_spawn_handler), + ASIO_MOVE_CAST(Function)(function), attributes); +} + +template +inline void spawn(const asio::io_context::strand& s, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes) +{ + asio::spawn(asio::bind_executor( + s, &detail::default_spawn_handler), + ASIO_MOVE_CAST(Function)(function), attributes); +} + +template +inline void spawn(ExecutionContext& ctx, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes, + typename enable_if::value>::type*) +{ + asio::spawn(ctx.get_executor(), + ASIO_MOVE_CAST(Function)(function), attributes); +} + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_SPAWN_HPP diff --git a/subprojects/asio/include/asio/impl/src.cpp b/subprojects/asio/include/asio/impl/src.cpp new file mode 100644 index 000000000..e8a5953db --- /dev/null +++ b/subprojects/asio/include/asio/impl/src.cpp @@ -0,0 +1,25 @@ +// +// impl/src.cpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#if defined(_MSC_VER) \ + || defined(__BORLANDC__) \ + || defined(__DMC__) +# pragma message ( \ + "This file is deprecated. " \ + "Please #include instead.") +#elif defined(__GNUC__) \ + || defined(__HP_aCC) \ + || defined(__SUNPRO_CC) \ + || defined(__IBMCPP__) +# warning "This file is deprecated." +# warning "Please #include instead." +#endif + +#include "asio/impl/src.hpp" diff --git a/subprojects/asio/include/asio/impl/src.hpp b/subprojects/asio/include/asio/impl/src.hpp new file mode 100644 index 000000000..a72637b94 --- /dev/null +++ b/subprojects/asio/include/asio/impl/src.hpp @@ -0,0 +1,82 @@ +// +// impl/src.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SRC_HPP +#define ASIO_IMPL_SRC_HPP + +#define ASIO_SOURCE + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HEADER_ONLY) +# error Do not compile Asio library source with ASIO_HEADER_ONLY defined +#endif + +#include "asio/impl/error.ipp" +#include "asio/impl/error_code.ipp" +#include "asio/impl/execution_context.ipp" +#include "asio/impl/executor.ipp" +#include "asio/impl/handler_alloc_hook.ipp" +#include "asio/impl/io_context.ipp" +#include "asio/impl/serial_port_base.ipp" +#include "asio/impl/system_context.ipp" +#include "asio/impl/thread_pool.ipp" +#include "asio/detail/impl/buffer_sequence_adapter.ipp" +#include "asio/detail/impl/descriptor_ops.ipp" +#include "asio/detail/impl/dev_poll_reactor.ipp" +#include "asio/detail/impl/epoll_reactor.ipp" +#include "asio/detail/impl/eventfd_select_interrupter.ipp" +#include "asio/detail/impl/handler_tracking.ipp" +#include "asio/detail/impl/kqueue_reactor.ipp" +#include "asio/detail/impl/null_event.ipp" +#include "asio/detail/impl/pipe_select_interrupter.ipp" +#include "asio/detail/impl/posix_event.ipp" +#include "asio/detail/impl/posix_mutex.ipp" +#include "asio/detail/impl/posix_thread.ipp" +#include "asio/detail/impl/posix_tss_ptr.ipp" +#include "asio/detail/impl/reactive_descriptor_service.ipp" +#include "asio/detail/impl/reactive_serial_port_service.ipp" +#include "asio/detail/impl/reactive_socket_service_base.ipp" +#include "asio/detail/impl/resolver_service_base.ipp" +#include "asio/detail/impl/scheduler.ipp" +#include "asio/detail/impl/select_reactor.ipp" +#include "asio/detail/impl/service_registry.ipp" +#include "asio/detail/impl/signal_set_service.ipp" +#include "asio/detail/impl/socket_ops.ipp" +#include "asio/detail/impl/socket_select_interrupter.ipp" +#include "asio/detail/impl/strand_executor_service.ipp" +#include "asio/detail/impl/strand_service.ipp" +#include "asio/detail/impl/throw_error.ipp" +#include "asio/detail/impl/timer_queue_ptime.ipp" +#include "asio/detail/impl/timer_queue_set.ipp" +#include "asio/detail/impl/win_iocp_handle_service.ipp" +#include "asio/detail/impl/win_iocp_io_context.ipp" +#include "asio/detail/impl/win_iocp_serial_port_service.ipp" +#include "asio/detail/impl/win_iocp_socket_service_base.ipp" +#include "asio/detail/impl/win_event.ipp" +#include "asio/detail/impl/win_mutex.ipp" +#include "asio/detail/impl/win_object_handle_service.ipp" +#include "asio/detail/impl/win_static_mutex.ipp" +#include "asio/detail/impl/win_thread.ipp" +#include "asio/detail/impl/win_tss_ptr.ipp" +#include "asio/detail/impl/winrt_ssocket_service_base.ipp" +#include "asio/detail/impl/winrt_timer_scheduler.ipp" +#include "asio/detail/impl/winsock_init.ipp" +#include "asio/generic/detail/impl/endpoint.ipp" +#include "asio/ip/impl/address.ipp" +#include "asio/ip/impl/address_v4.ipp" +#include "asio/ip/impl/address_v6.ipp" +#include "asio/ip/impl/host_name.ipp" +#include "asio/ip/impl/network_v4.ipp" +#include "asio/ip/impl/network_v6.ipp" +#include "asio/ip/detail/impl/endpoint.ipp" +#include "asio/local/detail/impl/endpoint.ipp" + +#endif // ASIO_IMPL_SRC_HPP diff --git a/subprojects/asio/include/asio/impl/system_context.hpp b/subprojects/asio/include/asio/impl/system_context.hpp new file mode 100644 index 000000000..87ffe76a2 --- /dev/null +++ b/subprojects/asio/include/asio/impl/system_context.hpp @@ -0,0 +1,34 @@ +// +// impl/system_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SYSTEM_CONTEXT_HPP +#define ASIO_IMPL_SYSTEM_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/system_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +inline system_context::executor_type +system_context::get_executor() ASIO_NOEXCEPT +{ + return system_executor(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_SYSTEM_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/impl/system_context.ipp b/subprojects/asio/include/asio/impl/system_context.ipp new file mode 100644 index 000000000..8ad5e4188 --- /dev/null +++ b/subprojects/asio/include/asio/impl/system_context.ipp @@ -0,0 +1,73 @@ +// +// impl/system_context.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SYSTEM_CONTEXT_IPP +#define ASIO_IMPL_SYSTEM_CONTEXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/system_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +struct system_context::thread_function +{ + detail::scheduler* scheduler_; + + void operator()() + { + asio::error_code ec; + scheduler_->run(ec); + } +}; + +system_context::system_context() + : scheduler_(use_service(*this)) +{ + scheduler_.work_started(); + + thread_function f = { &scheduler_ }; + std::size_t num_threads = detail::thread::hardware_concurrency() * 2; + threads_.create_threads(f, num_threads ? num_threads : 2); +} + +system_context::~system_context() +{ + scheduler_.work_finished(); + scheduler_.stop(); + threads_.join(); +} + +void system_context::stop() +{ + scheduler_.stop(); +} + +bool system_context::stopped() const ASIO_NOEXCEPT +{ + return scheduler_.stopped(); +} + +void system_context::join() +{ + scheduler_.work_finished(); + threads_.join(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_SYSTEM_CONTEXT_IPP diff --git a/subprojects/asio/include/asio/impl/system_executor.hpp b/subprojects/asio/include/asio/impl/system_executor.hpp new file mode 100644 index 000000000..ac4861f56 --- /dev/null +++ b/subprojects/asio/include/asio/impl/system_executor.hpp @@ -0,0 +1,85 @@ +// +// impl/system_executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SYSTEM_EXECUTOR_HPP +#define ASIO_IMPL_SYSTEM_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/executor_op.hpp" +#include "asio/detail/global.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/system_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +inline system_context& system_executor::context() const ASIO_NOEXCEPT +{ + return detail::global(); +} + +template +void system_executor::dispatch( + ASIO_MOVE_ARG(Function) f, const Allocator&) const +{ + typename decay::type tmp(ASIO_MOVE_CAST(Function)(f)); + asio_handler_invoke_helpers::invoke(tmp, tmp); +} + +template +void system_executor::post( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + system_context& ctx = detail::global(); + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((ctx, *p.p, + "system_executor", &this->context(), 0, "post")); + + ctx.scheduler_.post_immediate_completion(p.p, false); + p.v = p.p = 0; +} + +template +void system_executor::defer( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + system_context& ctx = detail::global(); + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((ctx, *p.p, + "system_executor", &this->context(), 0, "defer")); + + ctx.scheduler_.post_immediate_completion(p.p, true); + p.v = p.p = 0; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_SYSTEM_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/impl/thread_pool.hpp b/subprojects/asio/include/asio/impl/thread_pool.hpp new file mode 100644 index 000000000..058e3771b --- /dev/null +++ b/subprojects/asio/include/asio/impl/thread_pool.hpp @@ -0,0 +1,127 @@ +// +// impl/thread_pool.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_THREAD_POOL_HPP +#define ASIO_IMPL_THREAD_POOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/executor_op.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +inline thread_pool::executor_type +thread_pool::get_executor() ASIO_NOEXCEPT +{ + return executor_type(*this); +} + +inline thread_pool& +thread_pool::executor_type::context() const ASIO_NOEXCEPT +{ + return pool_; +} + +inline void +thread_pool::executor_type::on_work_started() const ASIO_NOEXCEPT +{ + pool_.scheduler_.work_started(); +} + +inline void thread_pool::executor_type::on_work_finished() +const ASIO_NOEXCEPT +{ + pool_.scheduler_.work_finished(); +} + +template +void thread_pool::executor_type::dispatch( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + // Invoke immediately if we are already inside the thread pool. + if (pool_.scheduler_.can_dispatch()) + { + // Make a local, non-const copy of the function. + function_type tmp(ASIO_MOVE_CAST(Function)(f)); + + detail::fenced_block b(detail::fenced_block::full); + asio_handler_invoke_helpers::invoke(tmp, tmp); + return; + } + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((pool_, *p.p, + "thread_pool", &this->context(), 0, "dispatch")); + + pool_.scheduler_.post_immediate_completion(p.p, false); + p.v = p.p = 0; +} + +template +void thread_pool::executor_type::post( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((pool_, *p.p, + "thread_pool", &this->context(), 0, "post")); + + pool_.scheduler_.post_immediate_completion(p.p, false); + p.v = p.p = 0; +} + +template +void thread_pool::executor_type::defer( + ASIO_MOVE_ARG(Function) f, const Allocator& a) const +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef detail::executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(f), a); + + ASIO_HANDLER_CREATION((pool_, *p.p, + "thread_pool", &this->context(), 0, "defer")); + + pool_.scheduler_.post_immediate_completion(p.p, true); + p.v = p.p = 0; +} + +inline bool +thread_pool::executor_type::running_in_this_thread() const ASIO_NOEXCEPT +{ + return pool_.scheduler_.can_dispatch(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_THREAD_POOL_HPP diff --git a/subprojects/asio/include/asio/impl/thread_pool.ipp b/subprojects/asio/include/asio/impl/thread_pool.ipp new file mode 100644 index 000000000..89583c14a --- /dev/null +++ b/subprojects/asio/include/asio/impl/thread_pool.ipp @@ -0,0 +1,76 @@ +// +// impl/thread_pool.ipp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_THREAD_POOL_IPP +#define ASIO_IMPL_THREAD_POOL_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/thread_pool.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +struct thread_pool::thread_function +{ + detail::scheduler* scheduler_; + + void operator()() + { + asio::error_code ec; + scheduler_->run(ec); + } +}; + +thread_pool::thread_pool() + : scheduler_(use_service(*this)) +{ + scheduler_.work_started(); + + thread_function f = { &scheduler_ }; + std::size_t num_threads = detail::thread::hardware_concurrency() * 2; + threads_.create_threads(f, num_threads ? num_threads : 2); +} + +thread_pool::thread_pool(std::size_t num_threads) + : scheduler_(use_service(*this)) +{ + scheduler_.work_started(); + + thread_function f = { &scheduler_ }; + threads_.create_threads(f, num_threads); +} + +thread_pool::~thread_pool() +{ + stop(); + join(); +} + +void thread_pool::stop() +{ + scheduler_.stop(); +} + +void thread_pool::join() +{ + scheduler_.work_finished(); + threads_.join(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_THREAD_POOL_IPP diff --git a/subprojects/asio/include/asio/impl/use_future.hpp b/subprojects/asio/include/asio/impl/use_future.hpp new file mode 100644 index 000000000..40bf7a47a --- /dev/null +++ b/subprojects/asio/include/asio/impl/use_future.hpp @@ -0,0 +1,937 @@ +// +// impl/use_future.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_USE_FUTURE_HPP +#define ASIO_IMPL_USE_FUTURE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/detail/memory.hpp" +#include "asio/error_code.hpp" +#include "asio/packaged_task.hpp" +#include "asio/system_error.hpp" +#include "asio/system_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +inline void promise_invoke_and_set(std::promise& p, + F& f, ASIO_MOVE_ARG(Args)... args) +{ +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + p.set_value(f(ASIO_MOVE_CAST(Args)(args)...)); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (...) + { + p.set_exception(std::current_exception()); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) +} + +template +inline void promise_invoke_and_set(std::promise& p, + F& f, ASIO_MOVE_ARG(Args)... args) +{ +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + f(ASIO_MOVE_CAST(Args)(args)...); + p.set_value(); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (...) + { + p.set_exception(std::current_exception()); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) +} + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +inline void promise_invoke_and_set(std::promise& p, F& f) +{ +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + p.set_value(f()); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (...) + { + p.set_exception(std::current_exception()); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) +} + +template +inline void promise_invoke_and_set(std::promise& p, F& f) +{ +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + f(); + p.set_value(); +#if !defined(ASIO_NO_EXCEPTIONS) + } + catch (...) + { + p.set_exception(std::current_exception()); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) +} + +#if defined(ASIO_NO_EXCEPTIONS) + +#define ASIO_PRIVATE_PROMISE_INVOKE_DEF(n) \ + template \ + inline void promise_invoke_and_set(std::promise& p, \ + F& f, ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + p.set_value(f(ASIO_VARIADIC_MOVE_ARGS(n))); \ + } \ + \ + template \ + inline void promise_invoke_and_set(std::promise& p, \ + F& f, ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + f(ASIO_VARIADIC_MOVE_ARGS(n)); \ + p.set_value(); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_PROMISE_INVOKE_DEF) +#undef ASIO_PRIVATE_PROMISE_INVOKE_DEF + +#else // defined(ASIO_NO_EXCEPTIONS) + +#define ASIO_PRIVATE_PROMISE_INVOKE_DEF(n) \ + template \ + inline void promise_invoke_and_set(std::promise& p, \ + F& f, ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + try \ + { \ + p.set_value(f(ASIO_VARIADIC_MOVE_ARGS(n))); \ + } \ + catch (...) \ + { \ + p.set_exception(std::current_exception()); \ + } \ + } \ + \ + template \ + inline void promise_invoke_and_set(std::promise& p, \ + F& f, ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + try \ + { \ + f(ASIO_VARIADIC_MOVE_ARGS(n)); \ + p.set_value(); \ + } \ + catch (...) \ + { \ + p.set_exception(std::current_exception()); \ + } \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_PROMISE_INVOKE_DEF) +#undef ASIO_PRIVATE_PROMISE_INVOKE_DEF + +#endif // defined(ASIO_NO_EXCEPTIONS) + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +// A function object adapter to invoke a nullary function object and capture +// any exception thrown into a promise. +template +class promise_invoker +{ +public: + promise_invoker(const shared_ptr >& p, + ASIO_MOVE_ARG(F) f) + : p_(p), f_(ASIO_MOVE_CAST(F)(f)) + { + } + + void operator()() + { +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + f_(); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (...) + { + p_->set_exception(std::current_exception()); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) + } + +private: + shared_ptr > p_; + typename decay::type f_; +}; + +// An executor that adapts the system_executor to capture any exeption thrown +// by a submitted function object and save it into a promise. +template +class promise_executor +{ +public: + explicit promise_executor(const shared_ptr >& p) + : p_(p) + { + } + + execution_context& context() const ASIO_NOEXCEPT + { + return system_executor().context(); + } + + void on_work_started() const ASIO_NOEXCEPT {} + void on_work_finished() const ASIO_NOEXCEPT {} + + template + void dispatch(ASIO_MOVE_ARG(F) f, const A&) const + { + promise_invoker(p_, ASIO_MOVE_CAST(F)(f))(); + } + + template + void post(ASIO_MOVE_ARG(F) f, const A& a) const + { + system_executor().post( + promise_invoker(p_, ASIO_MOVE_CAST(F)(f)), a); + } + + template + void defer(ASIO_MOVE_ARG(F) f, const A& a) const + { + system_executor().defer( + promise_invoker(p_, ASIO_MOVE_CAST(F)(f)), a); + } + + friend bool operator==(const promise_executor& a, + const promise_executor& b) ASIO_NOEXCEPT + { + return a.p_ == b.p_; + } + + friend bool operator!=(const promise_executor& a, + const promise_executor& b) ASIO_NOEXCEPT + { + return a.p_ != b.p_; + } + +private: + shared_ptr > p_; +}; + +// The base class for all completion handlers that create promises. +template +class promise_creator +{ +public: + typedef promise_executor executor_type; + + executor_type get_executor() const ASIO_NOEXCEPT + { + return executor_type(p_); + } + + typedef std::future future_type; + + future_type get_future() + { + return p_->get_future(); + } + +protected: + template + void create_promise(const Allocator& a) + { + ASIO_REBIND_ALLOC(Allocator, char) b(a); + p_ = std::allocate_shared>(b, std::allocator_arg, b); + } + + shared_ptr > p_; +}; + +// For completion signature void(). +class promise_handler_0 + : public promise_creator +{ +public: + void operator()() + { + this->p_->set_value(); + } +}; + +// For completion signature void(error_code). +class promise_handler_ec_0 + : public promise_creator +{ +public: + void operator()(const asio::error_code& ec) + { + if (ec) + { + this->p_->set_exception( + std::make_exception_ptr( + asio::system_error(ec))); + } + else + { + this->p_->set_value(); + } + } +}; + +// For completion signature void(exception_ptr). +class promise_handler_ex_0 + : public promise_creator +{ +public: + void operator()(const std::exception_ptr& ex) + { + if (ex) + { + this->p_->set_exception(ex); + } + else + { + this->p_->set_value(); + } + } +}; + +// For completion signature void(T). +template +class promise_handler_1 + : public promise_creator +{ +public: + template + void operator()(ASIO_MOVE_ARG(Arg) arg) + { + this->p_->set_value(ASIO_MOVE_CAST(Arg)(arg)); + } +}; + +// For completion signature void(error_code, T). +template +class promise_handler_ec_1 + : public promise_creator +{ +public: + template + void operator()(const asio::error_code& ec, + ASIO_MOVE_ARG(Arg) arg) + { + if (ec) + { + this->p_->set_exception( + std::make_exception_ptr( + asio::system_error(ec))); + } + else + this->p_->set_value(ASIO_MOVE_CAST(Arg)(arg)); + } +}; + +// For completion signature void(exception_ptr, T). +template +class promise_handler_ex_1 + : public promise_creator +{ +public: + template + void operator()(const std::exception_ptr& ex, + ASIO_MOVE_ARG(Arg) arg) + { + if (ex) + this->p_->set_exception(ex); + else + this->p_->set_value(ASIO_MOVE_CAST(Arg)(arg)); + } +}; + +// For completion signature void(T1, ..., Tn); +template +class promise_handler_n + : public promise_creator +{ +public: +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()(ASIO_MOVE_ARG(Args)... args) + { + this->p_->set_value( + std::forward_as_tuple( + ASIO_MOVE_CAST(Args)(args)...)); + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_CALL_OP_DEF(n) \ + template \ + void operator()(ASIO_VARIADIC_MOVE_PARAMS(n)) \ + {\ + this->p_->set_value( \ + std::forward_as_tuple( \ + ASIO_VARIADIC_MOVE_ARGS(n))); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CALL_OP_DEF) +#undef ASIO_PRIVATE_CALL_OP_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +}; + +// For completion signature void(error_code, T1, ..., Tn); +template +class promise_handler_ec_n + : public promise_creator +{ +public: +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()(const asio::error_code& ec, + ASIO_MOVE_ARG(Args)... args) + { + if (ec) + { + this->p_->set_exception( + std::make_exception_ptr( + asio::system_error(ec))); + } + else + { + this->p_->set_value( + std::forward_as_tuple( + ASIO_MOVE_CAST(Args)(args)...)); + } + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_CALL_OP_DEF(n) \ + template \ + void operator()(const asio::error_code& ec, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + {\ + if (ec) \ + { \ + this->p_->set_exception( \ + std::make_exception_ptr( \ + asio::system_error(ec))); \ + } \ + else \ + { \ + this->p_->set_value( \ + std::forward_as_tuple( \ + ASIO_VARIADIC_MOVE_ARGS(n))); \ + } \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CALL_OP_DEF) +#undef ASIO_PRIVATE_CALL_OP_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +}; + +// For completion signature void(exception_ptr, T1, ..., Tn); +template +class promise_handler_ex_n + : public promise_creator +{ +public: +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()(const std::exception_ptr& ex, + ASIO_MOVE_ARG(Args)... args) + { + if (ex) + this->p_->set_exception(ex); + else + { + this->p_->set_value( + std::forward_as_tuple( + ASIO_MOVE_CAST(Args)(args)...)); + } + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_CALL_OP_DEF(n) \ + template \ + void operator()(const std::exception_ptr& ex, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + {\ + if (ex) \ + this->p_->set_exception(ex); \ + else \ + { \ + this->p_->set_value( \ + std::forward_as_tuple( \ + ASIO_VARIADIC_MOVE_ARGS(n))); \ + } \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CALL_OP_DEF) +#undef ASIO_PRIVATE_CALL_OP_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +}; + +// Helper template to choose the appropriate concrete promise handler +// implementation based on the supplied completion signature. +template class promise_handler_selector; + +template <> +class promise_handler_selector + : public promise_handler_0 {}; + +template <> +class promise_handler_selector + : public promise_handler_ec_0 {}; + +template <> +class promise_handler_selector + : public promise_handler_ex_0 {}; + +template +class promise_handler_selector + : public promise_handler_1 {}; + +template +class promise_handler_selector + : public promise_handler_ec_1 {}; + +template +class promise_handler_selector + : public promise_handler_ex_1 {}; + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +class promise_handler_selector + : public promise_handler_n > {}; + +template +class promise_handler_selector + : public promise_handler_ec_n > {}; + +template +class promise_handler_selector + : public promise_handler_ex_n > {}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_PROMISE_SELECTOR_DEF(n) \ + template \ + class promise_handler_selector< \ + void(Arg, ASIO_VARIADIC_TARGS(n))> \ + : public promise_handler_n< \ + std::tuple > {}; \ + \ + template \ + class promise_handler_selector< \ + void(asio::error_code, Arg, ASIO_VARIADIC_TARGS(n))> \ + : public promise_handler_ec_n< \ + std::tuple > {}; \ + \ + template \ + class promise_handler_selector< \ + void(std::exception_ptr, Arg, ASIO_VARIADIC_TARGS(n))> \ + : public promise_handler_ex_n< \ + std::tuple > {}; \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_PROMISE_SELECTOR_DEF) +#undef ASIO_PRIVATE_PROMISE_SELECTOR_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +// Completion handlers produced from the use_future completion token, when not +// using use_future::operator(). +template +class promise_handler + : public promise_handler_selector +{ +public: + typedef Allocator allocator_type; + typedef void result_type; + + promise_handler(use_future_t u) + : allocator_(u.get_allocator()) + { + this->create_promise(allocator_); + } + + allocator_type get_allocator() const ASIO_NOEXCEPT + { + return allocator_; + } + +private: + Allocator allocator_; +}; + +template +inline void asio_handler_invoke(Function& f, + promise_handler* h) +{ + typename promise_handler::executor_type + ex(h->get_executor()); + ex.dispatch(ASIO_MOVE_CAST(Function)(f), std::allocator()); +} + +template +inline void asio_handler_invoke(const Function& f, + promise_handler* h) +{ + typename promise_handler::executor_type + ex(h->get_executor()); + ex.dispatch(f, std::allocator()); +} + +// Helper base class for async_result specialisation. +template +class promise_async_result +{ +public: + typedef promise_handler completion_handler_type; + typedef typename completion_handler_type::future_type return_type; + + explicit promise_async_result(completion_handler_type& h) + : future_(h.get_future()) + { + } + + return_type get() + { + return ASIO_MOVE_CAST(return_type)(future_); + } + +private: + return_type future_; +}; + +// Return value from use_future::operator(). +template +class packaged_token +{ +public: + packaged_token(Function f, const Allocator& a) + : function_(ASIO_MOVE_CAST(Function)(f)), + allocator_(a) + { + } + +//private: + Function function_; + Allocator allocator_; +}; + +// Completion handlers produced from the use_future completion token, when +// using use_future::operator(). +template +class packaged_handler + : public promise_creator +{ +public: + typedef Allocator allocator_type; + typedef void result_type; + + packaged_handler(packaged_token t) + : function_(ASIO_MOVE_CAST(Function)(t.function_)), + allocator_(t.allocator_) + { + this->create_promise(allocator_); + } + + allocator_type get_allocator() const ASIO_NOEXCEPT + { + return allocator_; + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()(ASIO_MOVE_ARG(Args)... args) + { + (promise_invoke_and_set)(*this->p_, + function_, ASIO_MOVE_CAST(Args)(args)...); + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + void operator()() + { + (promise_invoke_and_set)(*this->p_, function_); + } + +#define ASIO_PRIVATE_CALL_OP_DEF(n) \ + template \ + void operator()(ASIO_VARIADIC_MOVE_PARAMS(n)) \ + {\ + (promise_invoke_and_set)(*this->p_, \ + function_, ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CALL_OP_DEF) +#undef ASIO_PRIVATE_CALL_OP_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +private: + Function function_; + Allocator allocator_; +}; + +template +inline void asio_handler_invoke(Function& f, + packaged_handler* h) +{ + typename packaged_handler::executor_type + ex(h->get_executor()); + ex.dispatch(ASIO_MOVE_CAST(Function)(f), std::allocator()); +} + +template +inline void asio_handler_invoke(const Function& f, + packaged_handler* h) +{ + typename packaged_handler::executor_type + ex(h->get_executor()); + ex.dispatch(f, std::allocator()); +} + +// Helper base class for async_result specialisation. +template +class packaged_async_result +{ +public: + typedef packaged_handler completion_handler_type; + typedef typename completion_handler_type::future_type return_type; + + explicit packaged_async_result(completion_handler_type& h) + : future_(h.get_future()) + { + } + + return_type get() + { + return ASIO_MOVE_CAST(return_type)(future_); + } + +private: + return_type future_; +}; + +} // namespace detail + +template template +inline detail::packaged_token::type, Allocator> +use_future_t::operator()(ASIO_MOVE_ARG(Function) f) const +{ + return detail::packaged_token::type, Allocator>( + ASIO_MOVE_CAST(Function)(f), allocator_); +} + +#if !defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +class async_result, Result(Args...)> + : public detail::promise_async_result< + void(typename decay::type...), Allocator> +{ +public: + explicit async_result( + typename detail::promise_async_result::type...), + Allocator>::completion_handler_type& h) + : detail::promise_async_result< + void(typename decay::type...), Allocator>(h) + { + } +}; + +template +class async_result, Result(Args...)> + : public detail::packaged_async_result::type> +{ +public: + explicit async_result( + typename detail::packaged_async_result::type>::completion_handler_type& h) + : detail::packaged_async_result::type>(h) + { + } +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +class async_result, Result()> + : public detail::promise_async_result +{ +public: + explicit async_result( + typename detail::promise_async_result< + void(), Allocator>::completion_handler_type& h) + : detail::promise_async_result(h) + { + } +}; + +template +class async_result, Result()> + : public detail::packaged_async_result::type> +{ +public: + explicit async_result( + typename detail::packaged_async_result::type>::completion_handler_type& h) + : detail::packaged_async_result::type>(h) + { + } +}; + +#define ASIO_PRIVATE_ASYNC_RESULT_DEF(n) \ + template \ + class async_result, \ + Result(ASIO_VARIADIC_TARGS(n))> \ + : public detail::promise_async_result< \ + void(ASIO_VARIADIC_DECAY(n)), Allocator> \ + { \ + public: \ + explicit async_result( \ + typename detail::promise_async_result< \ + void(ASIO_VARIADIC_DECAY(n)), \ + Allocator>::completion_handler_type& h) \ + : detail::promise_async_result< \ + void(ASIO_VARIADIC_DECAY(n)), Allocator>(h) \ + { \ + } \ + }; \ + \ + template \ + class async_result, \ + Result(ASIO_VARIADIC_TARGS(n))> \ + : public detail::packaged_async_result::type> \ + { \ + public: \ + explicit async_result( \ + typename detail::packaged_async_result::type \ + >::completion_handler_type& h) \ + : detail::packaged_async_result::type>(h) \ + { \ + } \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ASYNC_RESULT_DEF) +#undef ASIO_PRIVATE_ASYNC_RESULT_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#if !defined(ASIO_NO_DEPRECATED) + +template +struct handler_type, Signature> +{ + typedef typename async_result, + Signature>::completion_handler_type type; +}; + +template +class async_result > + : public detail::promise_async_result +{ +public: + typedef typename detail::promise_async_result< + Signature, Allocator>::return_type type; + + explicit async_result( + typename detail::promise_async_result< + Signature, Allocator>::completion_handler_type& h) + : detail::promise_async_result(h) + { + } +}; + +template +struct handler_type, Signature> +{ + typedef typename async_result, + Signature>::completion_handler_type type; +}; + +template +class async_result > + : public detail::packaged_async_result +{ +public: + typedef typename detail::packaged_async_result< + Function, Allocator, Result>::return_type type; + + explicit async_result( + typename detail::packaged_async_result< + Function, Allocator, Result>::completion_handler_type& h) + : detail::packaged_async_result(h) + { + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_USE_FUTURE_HPP diff --git a/subprojects/asio/include/asio/impl/write.hpp b/subprojects/asio/include/asio/impl/write.hpp new file mode 100644 index 000000000..d07e8d3e0 --- /dev/null +++ b/subprojects/asio/include/asio/impl/write.hpp @@ -0,0 +1,674 @@ +// +// impl/write.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_WRITE_HPP +#define ASIO_IMPL_WRITE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/dependent_type.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + template + std::size_t write_buffer_sequence(SyncWriteStream& s, + const ConstBufferSequence& buffers, const ConstBufferIterator&, + CompletionCondition completion_condition, asio::error_code& ec) + { + ec = asio::error_code(); + asio::detail::consuming_buffers tmp(buffers); + while (!tmp.empty()) + { + if (std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, tmp.total_consumed()))) + tmp.consume(s.write_some(tmp.prepare(max_size), ec)); + else + break; + } + return tmp.total_consumed();; + } +} // namespace detail + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_const_buffer_sequence::value + >::type*) +{ + return detail::write_buffer_sequence(s, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, ec); +} + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + typename enable_if< + is_const_buffer_sequence::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec); + asio::detail::throw_error(ec, "write"); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + asio::error_code& ec, + typename enable_if< + is_const_buffer_sequence::value + >::type*) +{ + return write(s, buffers, transfer_all(), ec); +} + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, + typename enable_if< + is_const_buffer_sequence::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, buffers, completion_condition, ec); + asio::detail::throw_error(ec, "write"); + return bytes_transferred; +} + +template +std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + typename decay::type b( + ASIO_MOVE_CAST(DynamicBuffer)(buffers)); + + std::size_t bytes_transferred = write(s, b.data(), completion_condition, ec); + b.consume(bytes_transferred); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), + transfer_all(), ec); + asio::detail::throw_error(ec, "write"); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + return write(s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + transfer_all(), ec); +} + +template +inline std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), + completion_condition, ec); + asio::detail::throw_error(ec, "write"); + return bytes_transferred; +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + return write(s, basic_streambuf_ref(b), completion_condition, ec); +} + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b) +{ + return write(s, basic_streambuf_ref(b)); +} + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b, + asio::error_code& ec) +{ + return write(s, basic_streambuf_ref(b), ec); +} + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + return write(s, basic_streambuf_ref(b), completion_condition); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +namespace detail +{ + template + class write_op + : detail::base_from_completion_cond + { + public: + write_op(AsyncWriteStream& stream, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler& handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffers_(buffers), + start_(0), + handler_(ASIO_MOVE_CAST(WriteHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + write_op(const write_op& other) + : detail::base_from_completion_cond(other), + stream_(other.stream_), + buffers_(other.buffers_), + start_(other.start_), + handler_(other.handler_) + { + } + + write_op(write_op&& other) + : detail::base_from_completion_cond(other), + stream_(other.stream_), + buffers_(other.buffers_), + start_(other.start_), + handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size; + switch (start_ = start) + { + case 1: + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + do + { + stream_.async_write_some(buffers_.prepare(max_size), + ASIO_MOVE_CAST(write_op)(*this)); + return; default: + buffers_.consume(bytes_transferred); + if ((!ec && bytes_transferred == 0) || buffers_.empty()) + break; + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + } while (max_size > 0); + + handler_(ec, buffers_.total_consumed()); + } + } + + //private: + AsyncWriteStream& stream_; + asio::detail::consuming_buffers buffers_; + int start_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + write_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + write_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void start_write_buffer_sequence_op(AsyncWriteStream& stream, + const ConstBufferSequence& buffers, const ConstBufferIterator&, + CompletionCondition completion_condition, WriteHandler& handler) + { + detail::write_op( + stream, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); + } + +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::write_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::write_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::write_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::write_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_const_buffer_sequence::value + >::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + detail::start_write_buffer_sequence_op(s, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, + init.completion_handler); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_const_buffer_sequence::value + >::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + detail::start_write_buffer_sequence_op(s, buffers, + asio::buffer_sequence_begin(buffers), transfer_all(), + init.completion_handler); + + return init.result.get(); +} + +namespace detail +{ + template + class write_dynbuf_op + { + public: + template + write_dynbuf_op(AsyncWriteStream& stream, + ASIO_MOVE_ARG(BufferSequence) buffers, + CompletionCondition completion_condition, WriteHandler& handler) + : stream_(stream), + buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)), + completion_condition_( + ASIO_MOVE_CAST(CompletionCondition)(completion_condition)), + handler_(ASIO_MOVE_CAST(WriteHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + write_dynbuf_op(const write_dynbuf_op& other) + : stream_(other.stream_), + buffers_(other.buffers_), + completion_condition_(other.completion_condition_), + handler_(other.handler_) + { + } + + write_dynbuf_op(write_dynbuf_op&& other) + : stream_(other.stream_), + buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)), + completion_condition_( + ASIO_MOVE_CAST(CompletionCondition)( + other.completion_condition_)), + handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + switch (start) + { + case 1: + async_write(stream_, buffers_.data(), completion_condition_, + ASIO_MOVE_CAST(write_dynbuf_op)(*this)); + return; default: + buffers_.consume(bytes_transferred); + handler_(ec, static_cast(bytes_transferred)); + } + } + + //private: + AsyncWriteStream& stream_; + DynamicBuffer buffers_; + CompletionCondition completion_condition_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_dynbuf_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_dynbuf_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + write_dynbuf_op* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + write_dynbuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_dynbuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::write_dynbuf_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::write_dynbuf_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::write_dynbuf_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::write_dynbuf_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + return async_write(s, + ASIO_MOVE_CAST(DynamicBuffer)(buffers), + transfer_all(), ASIO_MOVE_CAST(WriteHandler)(handler)); +} + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type*) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + detail::write_dynbuf_op::type, + CompletionCondition, ASIO_HANDLER_TYPE( + WriteHandler, void (asio::error_code, std::size_t))>( + s, ASIO_MOVE_CAST(DynamicBuffer)(buffers), + completion_condition, init.completion_handler)( + asio::error_code(), 0, 1); + + return init.result.get(); +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, + asio::basic_streambuf& b, + ASIO_MOVE_ARG(WriteHandler) handler) +{ + return async_write(s, basic_streambuf_ref(b), + ASIO_MOVE_CAST(WriteHandler)(handler)); +} + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler) +{ + return async_write(s, basic_streambuf_ref(b), + completion_condition, ASIO_MOVE_CAST(WriteHandler)(handler)); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_WRITE_HPP diff --git a/subprojects/asio/include/asio/impl/write_at.hpp b/subprojects/asio/include/asio/impl/write_at.hpp new file mode 100644 index 000000000..cc6f336f1 --- /dev/null +++ b/subprojects/asio/include/asio/impl/write_at.hpp @@ -0,0 +1,572 @@ +// +// impl/write_at.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_WRITE_AT_HPP +#define ASIO_IMPL_WRITE_AT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/dependent_type.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + template + std::size_t write_at_buffer_sequence(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + const ConstBufferIterator&, CompletionCondition completion_condition, + asio::error_code& ec) + { + ec = asio::error_code(); + asio::detail::consuming_buffers tmp(buffers); + while (!tmp.empty()) + { + if (std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, tmp.total_consumed()))) + { + tmp.consume(d.write_some_at(offset + tmp.total_consumed(), + tmp.prepare(max_size), ec)); + } + else + break; + } + return tmp.total_consumed();; + } +} // namespace detail + +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + return detail::write_at_buffer_sequence(d, offset, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, ec); +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, buffers, transfer_all(), ec); + asio::detail::throw_error(ec, "write_at"); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + asio::error_code& ec) +{ + return write_at(d, offset, buffers, transfer_all(), ec); +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, buffers, completion_condition, ec); + asio::detail::throw_error(ec, "write_at"); + return bytes_transferred; +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + std::size_t bytes_transferred = write_at( + d, offset, b.data(), completion_condition, ec); + b.consume(bytes_transferred); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec); + asio::detail::throw_error(ec, "write_at"); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, asio::basic_streambuf& b, + asio::error_code& ec) +{ + return write_at(d, offset, b, transfer_all(), ec); +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, b, completion_condition, ec); + asio::detail::throw_error(ec, "write_at"); + return bytes_transferred; +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +namespace detail +{ + template + class write_at_op + : detail::base_from_completion_cond + { + public: + write_at_op(AsyncRandomAccessWriteDevice& device, + uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler& handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + buffers_(buffers), + start_(0), + handler_(ASIO_MOVE_CAST(WriteHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + write_at_op(const write_at_op& other) + : detail::base_from_completion_cond(other), + device_(other.device_), + offset_(other.offset_), + buffers_(other.buffers_), + start_(other.start_), + handler_(other.handler_) + { + } + + write_at_op(write_at_op&& other) + : detail::base_from_completion_cond(other), + device_(other.device_), + offset_(other.offset_), + buffers_(other.buffers_), + start_(other.start_), + handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size; + switch (start_ = start) + { + case 1: + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + do + { + device_.async_write_some_at( + offset_ + buffers_.total_consumed(), buffers_.prepare(max_size), + ASIO_MOVE_CAST(write_at_op)(*this)); + return; default: + buffers_.consume(bytes_transferred); + if ((!ec && bytes_transferred == 0) || buffers_.empty()) + break; + max_size = this->check_for_completion(ec, buffers_.total_consumed()); + } while (max_size > 0); + + handler_(ec, buffers_.total_consumed()); + } + } + + //private: + AsyncRandomAccessWriteDevice& device_; + uint64_t offset_; + asio::detail::consuming_buffers buffers_; + int start_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_at_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_at_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + write_at_op* this_handler) + { + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + write_at_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_at_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void start_write_at_buffer_sequence_op(AsyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + const ConstBufferIterator&, CompletionCondition completion_condition, + WriteHandler& handler) + { + detail::write_at_op( + d, offset, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::write_at_op, + Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::write_at_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::write_at_op, + Executor> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::write_at_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + detail::start_write_at_buffer_sequence_op(d, offset, buffers, + asio::buffer_sequence_begin(buffers), completion_condition, + init.completion_handler); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + detail::start_write_at_buffer_sequence_op(d, offset, buffers, + asio::buffer_sequence_begin(buffers), transfer_all(), + init.completion_handler); + + return init.result.get(); +} + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +namespace detail +{ + template + class write_at_streambuf_op + { + public: + write_at_streambuf_op( + asio::basic_streambuf& streambuf, + WriteHandler& handler) + : streambuf_(streambuf), + handler_(ASIO_MOVE_CAST(WriteHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + write_at_streambuf_op(const write_at_streambuf_op& other) + : streambuf_(other.streambuf_), + handler_(other.handler_) + { + } + + write_at_streambuf_op(write_at_streambuf_op&& other) + : streambuf_(other.streambuf_), + handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(const asio::error_code& ec, + const std::size_t bytes_transferred) + { + streambuf_.consume(bytes_transferred); + handler_(ec, bytes_transferred); + } + + //private: + asio::basic_streambuf& streambuf_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_at_streambuf_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_at_streambuf_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline bool asio_handler_is_continuation( + write_at_streambuf_op* this_handler) + { + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); + } + + template + inline void asio_handler_invoke(Function& function, + write_at_streambuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_at_streambuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } + + template + inline write_at_streambuf_op + make_write_at_streambuf_op( + asio::basic_streambuf& b, WriteHandler handler) + { + return write_at_streambuf_op(b, handler); + } +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct associated_allocator< + detail::write_at_streambuf_op, + Allocator1> +{ + typedef typename associated_allocator::type type; + + static type get( + const detail::write_at_streambuf_op& h, + const Allocator1& a = Allocator1()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + detail::write_at_streambuf_op, + Executor1> +{ + typedef typename associated_executor::type type; + + static type get( + const detail::write_at_streambuf_op& h, + const Executor1& ex = Executor1()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, + uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + async_write_at(d, offset, b.data(), completion_condition, + detail::write_at_streambuf_op( + b, init.completion_handler)); + + return init.result.get(); +} + +template +inline ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, + uint64_t offset, asio::basic_streambuf& b, + ASIO_MOVE_ARG(WriteHandler) handler) +{ + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + async_write_at(d, offset, b.data(), transfer_all(), + detail::write_at_streambuf_op( + b, init.completion_handler)); + + return init.result.get(); +} + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_WRITE_AT_HPP diff --git a/subprojects/asio/include/asio/io_context.hpp b/subprojects/asio/include/asio/io_context.hpp new file mode 100644 index 000000000..4d93be44b --- /dev/null +++ b/subprojects/asio/include/asio/io_context.hpp @@ -0,0 +1,876 @@ +// +// io_context.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_CONTEXT_HPP +#define ASIO_IO_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include "asio/async_result.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/wrapped_handler.hpp" +#include "asio/error_code.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_CHRONO) +# include "asio/detail/chrono.hpp" +#endif // defined(ASIO_HAS_CHRONO) + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/winsock_init.hpp" +#elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \ + || defined(__osf__) +# include "asio/detail/signal_init.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail { +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context io_context_impl; + class win_iocp_overlapped_ptr; +#else + typedef class scheduler io_context_impl; +#endif +} // namespace detail + +/// Provides core I/O functionality. +/** + * The io_context class provides the core I/O functionality for users of the + * asynchronous I/O objects, including: + * + * @li asio::ip::tcp::socket + * @li asio::ip::tcp::acceptor + * @li asio::ip::udp::socket + * @li asio::deadline_timer. + * + * The io_context class also includes facilities intended for developers of + * custom asynchronous services. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe, with the specific exceptions of the restart() + * and notify_fork() functions. Calling restart() while there are unfinished + * run(), run_one(), run_for(), run_until(), poll() or poll_one() calls results + * in undefined behaviour. The notify_fork() function should not be called + * while any io_context function, or any function on an I/O object that is + * associated with the io_context, is being called in another thread. + * + * @par Concepts: + * Dispatcher. + * + * @par Synchronous and asynchronous operations + * + * Synchronous operations on I/O objects implicitly run the io_context object + * for an individual operation. The io_context functions run(), run_one(), + * run_for(), run_until(), poll() or poll_one() must be called for the + * io_context to perform asynchronous operations on behalf of a C++ program. + * Notification that an asynchronous operation has completed is delivered by + * invocation of the associated handler. Handlers are invoked only by a thread + * that is currently calling any overload of run(), run_one(), run_for(), + * run_until(), poll() or poll_one() for the io_context. + * + * @par Effect of exceptions thrown from handlers + * + * If an exception is thrown from a handler, the exception is allowed to + * propagate through the throwing thread's invocation of run(), run_one(), + * run_for(), run_until(), poll() or poll_one(). No other threads that are + * calling any of these functions are affected. It is then the responsibility + * of the application to catch the exception. + * + * After the exception has been caught, the run(), run_one(), run_for(), + * run_until(), poll() or poll_one() call may be restarted @em without the need + * for an intervening call to restart(). This allows the thread to rejoin the + * io_context object's thread pool without impacting any other threads in the + * pool. + * + * For example: + * + * @code + * asio::io_context io_context; + * ... + * for (;;) + * { + * try + * { + * io_context.run(); + * break; // run() exited normally + * } + * catch (my_exception& e) + * { + * // Deal with exception as appropriate. + * } + * } + * @endcode + * + * @par Submitting arbitrary tasks to the io_context + * + * To submit functions to the io_context, use the @ref asio::dispatch, + * @ref asio::post or @ref asio::defer free functions. + * + * For example: + * + * @code void my_task() + * { + * ... + * } + * + * ... + * + * asio::io_context io_context; + * + * // Submit a function to the io_context. + * asio::post(io_context, my_task); + * + * // Submit a lambda object to the io_context. + * asio::post(io_context, + * []() + * { + * ... + * }); + * + * // Run the io_context until it runs out of work. + * io_context.run(); @endcode + * + * @par Stopping the io_context from running out of work + * + * Some applications may need to prevent an io_context object's run() call from + * returning when there is no more work to do. For example, the io_context may + * be being run in a background thread that is launched prior to the + * application's asynchronous operations. The run() call may be kept running by + * creating an object of type + * asio::executor_work_guard: + * + * @code asio::io_context io_context; + * asio::executor_work_guard + * = asio::make_work_guard(io_context); + * ... @endcode + * + * To effect a shutdown, the application will then need to call the io_context + * object's stop() member function. This will cause the io_context run() call + * to return as soon as possible, abandoning unfinished operations and without + * permitting ready handlers to be dispatched. + * + * Alternatively, if the application requires that all operations and handlers + * be allowed to finish normally, the work object may be explicitly reset. + * + * @code asio::io_context io_context; + * asio::executor_work_guard + * = asio::make_work_guard(io_context); + * ... + * work.reset(); // Allow run() to exit. @endcode + */ +class io_context + : public execution_context +{ +private: + typedef detail::io_context_impl impl_type; +#if defined(ASIO_HAS_IOCP) + friend class detail::win_iocp_overlapped_ptr; +#endif + +public: + class executor_type; + friend class executor_type; + +#if !defined(ASIO_NO_DEPRECATED) + class work; + friend class work; +#endif // !defined(ASIO_NO_DEPRECATED) + + class service; + +#if !defined(ASIO_NO_EXTENSIONS) + class strand; +#endif // !defined(ASIO_NO_EXTENSIONS) + + /// The type used to count the number of handlers executed by the context. + typedef std::size_t count_type; + + /// Constructor. + ASIO_DECL io_context(); + + /// Constructor. + /** + * Construct with a hint about the required level of concurrency. + * + * @param concurrency_hint A suggestion to the implementation on how many + * threads it should allow to run simultaneously. + */ + ASIO_DECL explicit io_context(int concurrency_hint); + + /// Destructor. + /** + * On destruction, the io_context performs the following sequence of + * operations: + * + * @li For each service object @c svc in the io_context set, in reverse order + * of the beginning of service object lifetime, performs + * @c svc->shutdown(). + * + * @li Uninvoked handler objects that were scheduled for deferred invocation + * on the io_context, or any associated strand, are destroyed. + * + * @li For each service object @c svc in the io_context set, in reverse order + * of the beginning of service object lifetime, performs + * delete static_cast(svc). + * + * @note The destruction sequence described above permits programs to + * simplify their resource management by using @c shared_ptr<>. Where an + * object's lifetime is tied to the lifetime of a connection (or some other + * sequence of asynchronous operations), a @c shared_ptr to the object would + * be bound into the handlers for all asynchronous operations associated with + * it. This works as follows: + * + * @li When a single connection ends, all associated asynchronous operations + * complete. The corresponding handler objects are destroyed, and all + * @c shared_ptr references to the objects are destroyed. + * + * @li To shut down the whole program, the io_context function stop() is + * called to terminate any run() calls as soon as possible. The io_context + * destructor defined above destroys all handlers, causing all @c shared_ptr + * references to all connection objects to be destroyed. + */ + ASIO_DECL ~io_context(); + + /// Obtains the executor associated with the io_context. + executor_type get_executor() ASIO_NOEXCEPT; + + /// Run the io_context object's event processing loop. + /** + * The run() function blocks until all work has finished and there are no + * more handlers to be dispatched, or until the io_context has been stopped. + * + * Multiple threads may call the run() function to set up a pool of threads + * from which the io_context may execute handlers. All threads that are + * waiting in the pool are equivalent and the io_context may choose any one + * of them to invoke a handler. + * + * A normal exit from the run() function implies that the io_context object + * is stopped (the stopped() function returns @c true). Subsequent calls to + * run(), run_one(), poll() or poll_one() will return immediately unless there + * is a prior call to restart(). + * + * @return The number of handlers that were executed. + * + * @note Calling the run() function from a thread that is currently calling + * one of run(), run_one(), run_for(), run_until(), poll() or poll_one() on + * the same io_context object may introduce the potential for deadlock. It is + * the caller's reponsibility to avoid this. + * + * The poll() function may also be used to dispatch ready handlers, but + * without blocking. + */ + ASIO_DECL count_type run(); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Run the io_context object's + /// event processing loop. + /** + * The run() function blocks until all work has finished and there are no + * more handlers to be dispatched, or until the io_context has been stopped. + * + * Multiple threads may call the run() function to set up a pool of threads + * from which the io_context may execute handlers. All threads that are + * waiting in the pool are equivalent and the io_context may choose any one + * of them to invoke a handler. + * + * A normal exit from the run() function implies that the io_context object + * is stopped (the stopped() function returns @c true). Subsequent calls to + * run(), run_one(), poll() or poll_one() will return immediately unless there + * is a prior call to restart(). + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of handlers that were executed. + * + * @note Calling the run() function from a thread that is currently calling + * one of run(), run_one(), run_for(), run_until(), poll() or poll_one() on + * the same io_context object may introduce the potential for deadlock. It is + * the caller's reponsibility to avoid this. + * + * The poll() function may also be used to dispatch ready handlers, but + * without blocking. + */ + ASIO_DECL count_type run(asio::error_code& ec); +#endif // !defined(ASIO_NO_DEPRECATED) + +#if defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + /// Run the io_context object's event processing loop for a specified + /// duration. + /** + * The run_for() function blocks until all work has finished and there are no + * more handlers to be dispatched, until the io_context has been stopped, or + * until the specified duration has elapsed. + * + * @param rel_time The duration for which the call may block. + * + * @return The number of handlers that were executed. + */ + template + std::size_t run_for(const chrono::duration& rel_time); + + /// Run the io_context object's event processing loop until a specified time. + /** + * The run_until() function blocks until all work has finished and there are + * no more handlers to be dispatched, until the io_context has been stopped, + * or until the specified time has been reached. + * + * @param abs_time The time point until which the call may block. + * + * @return The number of handlers that were executed. + */ + template + std::size_t run_until(const chrono::time_point& abs_time); +#endif // defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + + /// Run the io_context object's event processing loop to execute at most one + /// handler. + /** + * The run_one() function blocks until one handler has been dispatched, or + * until the io_context has been stopped. + * + * @return The number of handlers that were executed. A zero return value + * implies that the io_context object is stopped (the stopped() function + * returns @c true). Subsequent calls to run(), run_one(), poll() or + * poll_one() will return immediately unless there is a prior call to + * restart(). + * + * @note Calling the run_one() function from a thread that is currently + * calling one of run(), run_one(), run_for(), run_until(), poll() or + * poll_one() on the same io_context object may introduce the potential for + * deadlock. It is the caller's reponsibility to avoid this. + */ + ASIO_DECL count_type run_one(); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overlaod.) Run the io_context object's + /// event processing loop to execute at most one handler. + /** + * The run_one() function blocks until one handler has been dispatched, or + * until the io_context has been stopped. + * + * @return The number of handlers that were executed. A zero return value + * implies that the io_context object is stopped (the stopped() function + * returns @c true). Subsequent calls to run(), run_one(), poll() or + * poll_one() will return immediately unless there is a prior call to + * restart(). + * + * @return The number of handlers that were executed. + * + * @note Calling the run_one() function from a thread that is currently + * calling one of run(), run_one(), run_for(), run_until(), poll() or + * poll_one() on the same io_context object may introduce the potential for + * deadlock. It is the caller's reponsibility to avoid this. + */ + ASIO_DECL count_type run_one(asio::error_code& ec); +#endif // !defined(ASIO_NO_DEPRECATED) + +#if defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + /// Run the io_context object's event processing loop for a specified duration + /// to execute at most one handler. + /** + * The run_one_for() function blocks until one handler has been dispatched, + * until the io_context has been stopped, or until the specified duration has + * elapsed. + * + * @param rel_time The duration for which the call may block. + * + * @return The number of handlers that were executed. + */ + template + std::size_t run_one_for(const chrono::duration& rel_time); + + /// Run the io_context object's event processing loop until a specified time + /// to execute at most one handler. + /** + * The run_one_until() function blocks until one handler has been dispatched, + * until the io_context has been stopped, or until the specified time has + * been reached. + * + * @param abs_time The time point until which the call may block. + * + * @return The number of handlers that were executed. + */ + template + std::size_t run_one_until( + const chrono::time_point& abs_time); +#endif // defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + + /// Run the io_context object's event processing loop to execute ready + /// handlers. + /** + * The poll() function runs handlers that are ready to run, without blocking, + * until the io_context has been stopped or there are no more ready handlers. + * + * @return The number of handlers that were executed. + */ + ASIO_DECL count_type poll(); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Run the io_context object's + /// event processing loop to execute ready handlers. + /** + * The poll() function runs handlers that are ready to run, without blocking, + * until the io_context has been stopped or there are no more ready handlers. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of handlers that were executed. + */ + ASIO_DECL count_type poll(asio::error_code& ec); +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Run the io_context object's event processing loop to execute one ready + /// handler. + /** + * The poll_one() function runs at most one handler that is ready to run, + * without blocking. + * + * @return The number of handlers that were executed. + */ + ASIO_DECL count_type poll_one(); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Run the io_context object's + /// event processing loop to execute one ready handler. + /** + * The poll_one() function runs at most one handler that is ready to run, + * without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of handlers that were executed. + */ + ASIO_DECL count_type poll_one(asio::error_code& ec); +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Stop the io_context object's event processing loop. + /** + * This function does not block, but instead simply signals the io_context to + * stop. All invocations of its run() or run_one() member functions should + * return as soon as possible. Subsequent calls to run(), run_one(), poll() + * or poll_one() will return immediately until restart() is called. + */ + ASIO_DECL void stop(); + + /// Determine whether the io_context object has been stopped. + /** + * This function is used to determine whether an io_context object has been + * stopped, either through an explicit call to stop(), or due to running out + * of work. When an io_context object is stopped, calls to run(), run_one(), + * poll() or poll_one() will return immediately without invoking any + * handlers. + * + * @return @c true if the io_context object is stopped, otherwise @c false. + */ + ASIO_DECL bool stopped() const; + + /// Restart the io_context in preparation for a subsequent run() invocation. + /** + * This function must be called prior to any second or later set of + * invocations of the run(), run_one(), poll() or poll_one() functions when a + * previous invocation of these functions returned due to the io_context + * being stopped or running out of work. After a call to restart(), the + * io_context object's stopped() function will return @c false. + * + * This function must not be called while there are any unfinished calls to + * the run(), run_one(), poll() or poll_one() functions. + */ + ASIO_DECL void restart(); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use restart().) Reset the io_context in preparation for a + /// subsequent run() invocation. + /** + * This function must be called prior to any second or later set of + * invocations of the run(), run_one(), poll() or poll_one() functions when a + * previous invocation of these functions returned due to the io_context + * being stopped or running out of work. After a call to restart(), the + * io_context object's stopped() function will return @c false. + * + * This function must not be called while there are any unfinished calls to + * the run(), run_one(), poll() or poll_one() functions. + */ + void reset(); + + /// (Deprecated: Use asio::dispatch().) Request the io_context to + /// invoke the given handler. + /** + * This function is used to ask the io_context to execute the given handler. + * + * The io_context guarantees that the handler will only be called in a thread + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. The handler may be executed inside this function + * if the guarantee can be met. + * + * @param handler The handler to be called. The io_context will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + * + * @note This function throws an exception only if: + * + * @li the handler's @c asio_handler_allocate function; or + * + * @li the handler's copy constructor + * + * throws an exception. + */ + template + ASIO_INITFN_RESULT_TYPE(LegacyCompletionHandler, void ()) + dispatch(ASIO_MOVE_ARG(LegacyCompletionHandler) handler); + + /// (Deprecated: Use asio::post().) Request the io_context to invoke + /// the given handler and return immediately. + /** + * This function is used to ask the io_context to execute the given handler, + * but without allowing the io_context to call the handler from inside this + * function. + * + * The io_context guarantees that the handler will only be called in a thread + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. + * + * @param handler The handler to be called. The io_context will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + * + * @note This function throws an exception only if: + * + * @li the handler's @c asio_handler_allocate function; or + * + * @li the handler's copy constructor + * + * throws an exception. + */ + template + ASIO_INITFN_RESULT_TYPE(LegacyCompletionHandler, void ()) + post(ASIO_MOVE_ARG(LegacyCompletionHandler) handler); + + /// (Deprecated: Use asio::bind_executor().) Create a new handler that + /// automatically dispatches the wrapped handler on the io_context. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the io_context + * object's dispatch function. + * + * @param handler The handler to be wrapped. The io_context will make a copy + * of the handler object as required. The function signature of the handler + * must be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the io_context object's dispatch function. Given a function object with the + * signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code io_context.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code io_context.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler +#endif + wrap(Handler handler); +#endif // !defined(ASIO_NO_DEPRECATED) + +private: + // Helper function to add the implementation. + ASIO_DECL impl_type& add_impl(impl_type* impl); + + // Backwards compatible overload for use with services derived from + // io_context::service. + template + friend Service& use_service(io_context& ioc); + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + detail::winsock_init<> init_; +#elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \ + || defined(__osf__) + detail::signal_init<> init_; +#endif + + // The implementation. + impl_type& impl_; +}; + +/// Executor used to submit functions to an io_context. +class io_context::executor_type +{ +public: + /// Obtain the underlying execution context. + io_context& context() const ASIO_NOEXCEPT; + + /// Inform the io_context that it has some outstanding work to do. + /** + * This function is used to inform the io_context that some work has begun. + * This ensures that the io_context's run() and run_one() functions do not + * exit while the work is underway. + */ + void on_work_started() const ASIO_NOEXCEPT; + + /// Inform the io_context that some work is no longer outstanding. + /** + * This function is used to inform the io_context that some work has + * finished. Once the count of unfinished work reaches zero, the io_context + * is stopped and the run() and run_one() functions may exit. + */ + void on_work_finished() const ASIO_NOEXCEPT; + + /// Request the io_context to invoke the given function object. + /** + * This function is used to ask the io_context to execute the given function + * object. If the current thread is running the io_context, @c dispatch() + * executes the function before returning. Otherwise, the function will be + * scheduled to run on the io_context. + * + * @param f The function object to be called. The executor will make a copy + * of the handler object as required. The function signature of the function + * object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the io_context to invoke the given function object. + /** + * This function is used to ask the io_context to execute the given function + * object. The function object will never be executed inside @c post(). + * Instead, it will be scheduled to run on the io_context. + * + * @param f The function object to be called. The executor will make a copy + * of the handler object as required. The function signature of the function + * object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the io_context to invoke the given function object. + /** + * This function is used to ask the io_context to execute the given function + * object. The function object will never be executed inside @c defer(). + * Instead, it will be scheduled to run on the io_context. + * + * If the current thread belongs to the io_context, @c defer() will delay + * scheduling the function object until the current thread returns control to + * the pool. + * + * @param f The function object to be called. The executor will make a copy + * of the handler object as required. The function signature of the function + * object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Determine whether the io_context is running in the current thread. + /** + * @return @c true if the current thread is running the io_context. Otherwise + * returns @c false. + */ + bool running_in_this_thread() const ASIO_NOEXCEPT; + + /// Compare two executors for equality. + /** + * Two executors are equal if they refer to the same underlying io_context. + */ + friend bool operator==(const executor_type& a, + const executor_type& b) ASIO_NOEXCEPT + { + return &a.io_context_ == &b.io_context_; + } + + /// Compare two executors for inequality. + /** + * Two executors are equal if they refer to the same underlying io_context. + */ + friend bool operator!=(const executor_type& a, + const executor_type& b) ASIO_NOEXCEPT + { + return &a.io_context_ != &b.io_context_; + } + +private: + friend class io_context; + + // Constructor. + explicit executor_type(io_context& i) : io_context_(i) {} + + // The underlying io_context. + io_context& io_context_; +}; + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use executor_work_guard.) Class to inform the io_context when +/// it has work to do. +/** + * The work class is used to inform the io_context when work starts and + * finishes. This ensures that the io_context object's run() function will not + * exit while work is underway, and that it does exit when there is no + * unfinished work remaining. + * + * The work class is copy-constructible so that it may be used as a data member + * in a handler class. It is not assignable. + */ +class io_context::work +{ +public: + /// Constructor notifies the io_context that work is starting. + /** + * The constructor is used to inform the io_context that some work has begun. + * This ensures that the io_context object's run() function will not exit + * while the work is underway. + */ + explicit work(asio::io_context& io_context); + + /// Copy constructor notifies the io_context that work is starting. + /** + * The constructor is used to inform the io_context that some work has begun. + * This ensures that the io_context object's run() function will not exit + * while the work is underway. + */ + work(const work& other); + + /// Destructor notifies the io_context that the work is complete. + /** + * The destructor is used to inform the io_context that some work has + * finished. Once the count of unfinished work reaches zero, the io_context + * object's run() function is permitted to exit. + */ + ~work(); + + /// Get the io_context associated with the work. + asio::io_context& get_io_context(); + + /// (Deprecated: Use get_io_context().) Get the io_context associated with the + /// work. + asio::io_context& get_io_service(); + +private: + // Prevent assignment. + void operator=(const work& other); + + // The io_context implementation. + detail::io_context_impl& io_context_impl_; +}; +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Base class for all io_context services. +class io_context::service + : public execution_context::service +{ +public: + /// Get the io_context object that owns the service. + asio::io_context& get_io_context(); + +#if !defined(ASIO_NO_DEPRECATED) + /// Get the io_context object that owns the service. + asio::io_context& get_io_service(); +#endif // !defined(ASIO_NO_DEPRECATED) + +private: + /// Destroy all user-defined handler objects owned by the service. + ASIO_DECL virtual void shutdown(); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use shutdown().) Destroy all user-defined handler objects + /// owned by the service. + ASIO_DECL virtual void shutdown_service(); +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Handle notification of a fork-related event to perform any necessary + /// housekeeping. + /** + * This function is not a pure virtual so that services only have to + * implement it if necessary. The default implementation does nothing. + */ + ASIO_DECL virtual void notify_fork( + execution_context::fork_event event); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use notify_fork().) Handle notification of a fork-related + /// event to perform any necessary housekeeping. + /** + * This function is not a pure virtual so that services only have to + * implement it if necessary. The default implementation does nothing. + */ + ASIO_DECL virtual void fork_service( + execution_context::fork_event event); +#endif // !defined(ASIO_NO_DEPRECATED) + +protected: + /// Constructor. + /** + * @param owner The io_context object that owns the service. + */ + ASIO_DECL service(asio::io_context& owner); + + /// Destructor. + ASIO_DECL virtual ~service(); +}; + +namespace detail { + +// Special service base class to keep classes header-file only. +template +class service_base + : public asio::io_context::service +{ +public: + static asio::detail::service_id id; + + // Constructor. + service_base(asio::io_context& io_context) + : asio::io_context::service(io_context) + { + } +}; + +template +asio::detail::service_id service_base::id; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/io_context.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/io_context.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +// If both io_context.hpp and strand.hpp have been included, automatically +// include the header file needed for the io_context::strand class. +#if !defined(ASIO_NO_EXTENSIONS) +# if defined(ASIO_STRAND_HPP) +# include "asio/io_context_strand.hpp" +# endif // defined(ASIO_STRAND_HPP) +#endif // !defined(ASIO_NO_EXTENSIONS) + +#endif // ASIO_IO_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/io_context_strand.hpp b/subprojects/asio/include/asio/io_context_strand.hpp new file mode 100644 index 000000000..3c596f125 --- /dev/null +++ b/subprojects/asio/include/asio/io_context_strand.hpp @@ -0,0 +1,384 @@ +// +// io_context_strand.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_CONTEXT_STRAND_HPP +#define ASIO_IO_CONTEXT_STRAND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_EXTENSIONS) + +#include "asio/async_result.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/strand_service.hpp" +#include "asio/detail/wrapped_handler.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides serialised handler execution. +/** + * The io_context::strand class provides the ability to post and dispatch + * handlers with the guarantee that none of those handlers will execute + * concurrently. + * + * @par Order of handler invocation + * Given: + * + * @li a strand object @c s + * + * @li an object @c a meeting completion handler requirements + * + * @li an object @c a1 which is an arbitrary copy of @c a made by the + * implementation + * + * @li an object @c b meeting completion handler requirements + * + * @li an object @c b1 which is an arbitrary copy of @c b made by the + * implementation + * + * if any of the following conditions are true: + * + * @li @c s.post(a) happens-before @c s.post(b) + * + * @li @c s.post(a) happens-before @c s.dispatch(b), where the latter is + * performed outside the strand + * + * @li @c s.dispatch(a) happens-before @c s.post(b), where the former is + * performed outside the strand + * + * @li @c s.dispatch(a) happens-before @c s.dispatch(b), where both are + * performed outside the strand + * + * then @c asio_handler_invoke(a1, &a1) happens-before + * @c asio_handler_invoke(b1, &b1). + * + * Note that in the following case: + * @code async_op_1(..., s.wrap(a)); + * async_op_2(..., s.wrap(b)); @endcode + * the completion of the first async operation will perform @c s.dispatch(a), + * and the second will perform @c s.dispatch(b), but the order in which those + * are performed is unspecified. That is, you cannot state whether one + * happens-before the other. Therefore none of the above conditions are met and + * no ordering guarantee is made. + * + * @note The implementation makes no guarantee that handlers posted or + * dispatched through different @c strand objects will be invoked concurrently. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Dispatcher. + */ +class io_context::strand +{ +public: + /// Constructor. + /** + * Constructs the strand. + * + * @param io_context The io_context object that the strand will use to + * dispatch handlers that are ready to be run. + */ + explicit strand(asio::io_context& io_context) + : service_(asio::use_service< + asio::detail::strand_service>(io_context)) + { + service_.construct(impl_); + } + + /// Destructor. + /** + * Destroys a strand. + * + * Handlers posted through the strand that have not yet been invoked will + * still be dispatched in a way that meets the guarantee of non-concurrency. + */ + ~strand() + { + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use context().) Get the io_context associated with the + /// strand. + /** + * This function may be used to obtain the io_context object that the strand + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the strand will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return service_.get_io_context(); + } + + /// (Deprecated: Use context().) Get the io_context associated with the + /// strand. + /** + * This function may be used to obtain the io_context object that the strand + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the strand will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return service_.get_io_context(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Obtain the underlying execution context. + asio::io_context& context() const ASIO_NOEXCEPT + { + return service_.get_io_context(); + } + + /// Inform the strand that it has some outstanding work to do. + /** + * The strand delegates this call to its underlying io_context. + */ + void on_work_started() const ASIO_NOEXCEPT + { + context().get_executor().on_work_started(); + } + + /// Inform the strand that some work is no longer outstanding. + /** + * The strand delegates this call to its underlying io_context. + */ + void on_work_finished() const ASIO_NOEXCEPT + { + context().get_executor().on_work_finished(); + } + + /// Request the strand to invoke the given function object. + /** + * This function is used to ask the strand to execute the given function + * object on its underlying io_context. The function object will be executed + * inside this function if the strand is not otherwise busy and if the + * underlying io_context's executor's @c dispatch() function is also able to + * execute the function before returning. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const + { + typename decay::type tmp(ASIO_MOVE_CAST(Function)(f)); + service_.dispatch(impl_, tmp); + (void)a; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use asio::dispatch().) Request the strand to invoke + /// the given handler. + /** + * This function is used to ask the strand to execute the given handler. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The handler may be executed + * inside this function if the guarantee can be met. If this function is + * called from within a handler that was posted or dispatched through the same + * strand, then the new handler will be executed immediately. + * + * The strand's guarantee is in addition to the guarantee provided by the + * underlying io_context. The io_context guarantees that the handler will only + * be called in a thread in which the io_context's run member function is + * currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(LegacyCompletionHandler, void ()) + dispatch(ASIO_MOVE_ARG(LegacyCompletionHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a LegacyCompletionHandler. + ASIO_LEGACY_COMPLETION_HANDLER_CHECK( + LegacyCompletionHandler, handler) type_check; + + async_completion init(handler); + + service_.dispatch(impl_, init.completion_handler); + + return init.result.get(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Request the strand to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object will never be executed inside this function. + * Instead, it will be scheduled to run by the underlying io_context. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const + { + typename decay::type tmp(ASIO_MOVE_CAST(Function)(f)); + service_.post(impl_, tmp); + (void)a; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use asio::post().) Request the strand to invoke the + /// given handler and return immediately. + /** + * This function is used to ask the strand to execute the given handler, but + * without allowing the strand to call the handler from inside this function. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The strand's guarantee is in + * addition to the guarantee provided by the underlying io_context. The + * io_context guarantees that the handler will only be called in a thread in + * which the io_context's run member function is currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(LegacyCompletionHandler, void ()) + post(ASIO_MOVE_ARG(LegacyCompletionHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a LegacyCompletionHandler. + ASIO_LEGACY_COMPLETION_HANDLER_CHECK( + LegacyCompletionHandler, handler) type_check; + + async_completion init(handler); + + service_.post(impl_, init.completion_handler); + + return init.result.get(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Request the strand to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object will never be executed inside this function. + * Instead, it will be scheduled to run by the underlying io_context. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const + { + typename decay::type tmp(ASIO_MOVE_CAST(Function)(f)); + service_.post(impl_, tmp); + (void)a; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use asio::bind_executor().) Create a new handler that + /// automatically dispatches the wrapped handler on the strand. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the strand's + * dispatch function. + * + * @param handler The handler to be wrapped. The strand will make a copy of + * the handler object as required. The function signature of the handler must + * be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the strand's dispatch function. Given a function object with the signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code strand.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler +#endif + wrap(Handler handler) + { + return detail::wrapped_handler(*this, handler); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Determine whether the strand is running in the current thread. + /** + * @return @c true if the current thread is executing a handler that was + * submitted to the strand using post(), dispatch() or wrap(). Otherwise + * returns @c false. + */ + bool running_in_this_thread() const ASIO_NOEXCEPT + { + return service_.running_in_this_thread(impl_); + } + + /// Compare two strands for equality. + /** + * Two strands are equal if they refer to the same ordered, non-concurrent + * state. + */ + friend bool operator==(const strand& a, const strand& b) ASIO_NOEXCEPT + { + return a.impl_ == b.impl_; + } + + /// Compare two strands for inequality. + /** + * Two strands are equal if they refer to the same ordered, non-concurrent + * state. + */ + friend bool operator!=(const strand& a, const strand& b) ASIO_NOEXCEPT + { + return a.impl_ != b.impl_; + } + +private: + asio::detail::strand_service& service_; + mutable asio::detail::strand_service::implementation_type impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_EXTENSIONS) + +#endif // ASIO_IO_CONTEXT_STRAND_HPP diff --git a/subprojects/asio/include/asio/io_service.hpp b/subprojects/asio/include/asio/io_service.hpp new file mode 100644 index 000000000..ed05c8341 --- /dev/null +++ b/subprojects/asio/include/asio/io_service.hpp @@ -0,0 +1,33 @@ +// +// io_service.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_SERVICE_HPP +#define ASIO_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_NO_DEPRECATED) +/// Typedef for backwards compatibility. +typedef io_context io_service; +#endif // !defined(ASIO_NO_DEPRECATED) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IO_SERVICE_HPP diff --git a/subprojects/asio/include/asio/io_service_strand.hpp b/subprojects/asio/include/asio/io_service_strand.hpp new file mode 100644 index 000000000..7093f0e90 --- /dev/null +++ b/subprojects/asio/include/asio/io_service_strand.hpp @@ -0,0 +1,20 @@ +// +// io_service_strand.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_SERVICE_STRAND_HPP +#define ASIO_IO_SERVICE_STRAND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/io_context_strand.hpp" + +#endif // ASIO_IO_SERVICE_STRAND_HPP diff --git a/subprojects/asio/include/asio/ip/address.hpp b/subprojects/asio/include/asio/ip/address.hpp new file mode 100644 index 000000000..cf852a6d6 --- /dev/null +++ b/subprojects/asio/include/asio/ip/address.hpp @@ -0,0 +1,260 @@ +// +// ip/address.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_HPP +#define ASIO_IP_ADDRESS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/throw_exception.hpp" +#include "asio/detail/string_view.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error_code.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/ip/bad_address_cast.hpp" + +#if !defined(ASIO_NO_IOSTREAM) +# include +#endif // !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Implements version-independent IP addresses. +/** + * The asio::ip::address class provides the ability to use either IP + * version 4 or version 6 addresses. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address +{ +public: + /// Default constructor. + ASIO_DECL address(); + + /// Construct an address from an IPv4 address. + ASIO_DECL address(const asio::ip::address_v4& ipv4_address); + + /// Construct an address from an IPv6 address. + ASIO_DECL address(const asio::ip::address_v6& ipv6_address); + + /// Copy constructor. + ASIO_DECL address(const address& other); + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + ASIO_DECL address(address&& other); +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from another address. + ASIO_DECL address& operator=(const address& other); + +#if defined(ASIO_HAS_MOVE) + /// Move-assign from another address. + ASIO_DECL address& operator=(address&& other); +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from an IPv4 address. + ASIO_DECL address& operator=( + const asio::ip::address_v4& ipv4_address); + + /// Assign from an IPv6 address. + ASIO_DECL address& operator=( + const asio::ip::address_v6& ipv6_address); + + /// Get whether the address is an IP version 4 address. + bool is_v4() const + { + return type_ == ipv4; + } + + /// Get whether the address is an IP version 6 address. + bool is_v6() const + { + return type_ == ipv6; + } + + /// Get the address as an IP version 4 address. + ASIO_DECL asio::ip::address_v4 to_v4() const; + + /// Get the address as an IP version 6 address. + ASIO_DECL asio::ip::address_v6 to_v6() const; + + /// Get the address as a string. + ASIO_DECL std::string to_string() const; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use other overload.) Get the address as a string. + ASIO_DECL std::string to_string(asio::error_code& ec) const; + + /// (Deprecated: Use make_address().) Create an address from an IPv4 address + /// string in dotted decimal form, or from an IPv6 address in hexadecimal + /// notation. + static address from_string(const char* str); + + /// (Deprecated: Use make_address().) Create an address from an IPv4 address + /// string in dotted decimal form, or from an IPv6 address in hexadecimal + /// notation. + static address from_string(const char* str, asio::error_code& ec); + + /// (Deprecated: Use make_address().) Create an address from an IPv4 address + /// string in dotted decimal form, or from an IPv6 address in hexadecimal + /// notation. + static address from_string(const std::string& str); + + /// (Deprecated: Use make_address().) Create an address from an IPv4 address + /// string in dotted decimal form, or from an IPv6 address in hexadecimal + /// notation. + static address from_string( + const std::string& str, asio::error_code& ec); +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Determine whether the address is a loopback address. + ASIO_DECL bool is_loopback() const; + + /// Determine whether the address is unspecified. + ASIO_DECL bool is_unspecified() const; + + /// Determine whether the address is a multicast address. + ASIO_DECL bool is_multicast() const; + + /// Compare two addresses for equality. + ASIO_DECL friend bool operator==(const address& a1, const address& a2); + + /// Compare two addresses for inequality. + friend bool operator!=(const address& a1, const address& a2) + { + return !(a1 == a2); + } + + /// Compare addresses for ordering. + ASIO_DECL friend bool operator<(const address& a1, const address& a2); + + /// Compare addresses for ordering. + friend bool operator>(const address& a1, const address& a2) + { + return a2 < a1; + } + + /// Compare addresses for ordering. + friend bool operator<=(const address& a1, const address& a2) + { + return !(a2 < a1); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address& a1, const address& a2) + { + return !(a1 < a2); + } + +private: + // The type of the address. + enum { ipv4, ipv6 } type_; + + // The underlying IPv4 address. + asio::ip::address_v4 ipv4_address_; + + // The underlying IPv6 address. + asio::ip::address_v6 ipv6_address_; +}; + +/// Create an address from an IPv4 address string in dotted decimal form, +/// or from an IPv6 address in hexadecimal notation. +/** + * @relates address + */ +ASIO_DECL address make_address(const char* str); + +/// Create an address from an IPv4 address string in dotted decimal form, +/// or from an IPv6 address in hexadecimal notation. +/** + * @relates address + */ +ASIO_DECL address make_address( + const char* str, asio::error_code& ec); + +/// Create an address from an IPv4 address string in dotted decimal form, +/// or from an IPv6 address in hexadecimal notation. +/** + * @relates address + */ +ASIO_DECL address make_address(const std::string& str); + +/// Create an address from an IPv4 address string in dotted decimal form, +/// or from an IPv6 address in hexadecimal notation. +/** + * @relates address + */ +ASIO_DECL address make_address( + const std::string& str, asio::error_code& ec); + +#if defined(ASIO_HAS_STRING_VIEW) \ + || defined(GENERATING_DOCUMENTATION) + +/// Create an address from an IPv4 address string in dotted decimal form, +/// or from an IPv6 address in hexadecimal notation. +/** + * @relates address + */ +ASIO_DECL address make_address(string_view str); + +/// Create an address from an IPv4 address string in dotted decimal form, +/// or from an IPv6 address in hexadecimal notation. +/** + * @relates address + */ +ASIO_DECL address make_address( + string_view str, asio::error_code& ec); + +#endif // defined(ASIO_HAS_STRING_VIEW) + // || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_NO_IOSTREAM) + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address& addr); + +#endif // !defined(ASIO_NO_IOSTREAM) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/impl/address.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/address.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_ADDRESS_HPP diff --git a/subprojects/asio/include/asio/ip/address_v4.hpp b/subprojects/asio/include/asio/ip/address_v4.hpp new file mode 100644 index 000000000..4e1cc9a33 --- /dev/null +++ b/subprojects/asio/include/asio/ip/address_v4.hpp @@ -0,0 +1,329 @@ +// +// ip/address_v4.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V4_HPP +#define ASIO_IP_ADDRESS_V4_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/array.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/string_view.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/error_code.hpp" + +#if !defined(ASIO_NO_IOSTREAM) +# include +#endif // !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Implements IP version 4 style addresses. +/** + * The asio::ip::address_v4 class provides the ability to use and + * manipulate IP version 4 addresses. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address_v4 +{ +public: + /// The type used to represent an address as an unsigned integer. + typedef uint_least32_t uint_type; + + /// The type used to represent an address as an array of bytes. + /** + * @note This type is defined in terms of the C++0x template @c std::array + * when it is available. Otherwise, it uses @c boost:array. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef array bytes_type; +#else + typedef asio::detail::array bytes_type; +#endif + + /// Default constructor. + address_v4() + { + addr_.s_addr = 0; + } + + /// Construct an address from raw bytes. + ASIO_DECL explicit address_v4(const bytes_type& bytes); + + /// Construct an address from an unsigned integer in host byte order. + ASIO_DECL explicit address_v4(uint_type addr); + + /// Copy constructor. + address_v4(const address_v4& other) + : addr_(other.addr_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + address_v4(address_v4&& other) + : addr_(other.addr_) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from another address. + address_v4& operator=(const address_v4& other) + { + addr_ = other.addr_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move-assign from another address. + address_v4& operator=(address_v4&& other) + { + addr_ = other.addr_; + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// Get the address in bytes, in network byte order. + ASIO_DECL bytes_type to_bytes() const; + + /// Get the address as an unsigned integer in host byte order + ASIO_DECL uint_type to_uint() const; + +#if !defined(ASIO_NO_DEPRECATED) + /// Get the address as an unsigned long in host byte order + ASIO_DECL unsigned long to_ulong() const; +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the address as a string in dotted decimal format. + ASIO_DECL std::string to_string() const; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use other overload.) Get the address as a string in dotted + /// decimal format. + ASIO_DECL std::string to_string(asio::error_code& ec) const; + + /// (Deprecated: Use make_address_v4().) Create an address from an IP address + /// string in dotted decimal form. + static address_v4 from_string(const char* str); + + /// (Deprecated: Use make_address_v4().) Create an address from an IP address + /// string in dotted decimal form. + static address_v4 from_string( + const char* str, asio::error_code& ec); + + /// (Deprecated: Use make_address_v4().) Create an address from an IP address + /// string in dotted decimal form. + static address_v4 from_string(const std::string& str); + + /// (Deprecated: Use make_address_v4().) Create an address from an IP address + /// string in dotted decimal form. + static address_v4 from_string( + const std::string& str, asio::error_code& ec); +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Determine whether the address is a loopback address. + ASIO_DECL bool is_loopback() const; + + /// Determine whether the address is unspecified. + ASIO_DECL bool is_unspecified() const; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use network_v4 class.) Determine whether the address is a + /// class A address. + ASIO_DECL bool is_class_a() const; + + /// (Deprecated: Use network_v4 class.) Determine whether the address is a + /// class B address. + ASIO_DECL bool is_class_b() const; + + /// (Deprecated: Use network_v4 class.) Determine whether the address is a + /// class C address. + ASIO_DECL bool is_class_c() const; +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Determine whether the address is a multicast address. + ASIO_DECL bool is_multicast() const; + + /// Compare two addresses for equality. + friend bool operator==(const address_v4& a1, const address_v4& a2) + { + return a1.addr_.s_addr == a2.addr_.s_addr; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address_v4& a1, const address_v4& a2) + { + return a1.addr_.s_addr != a2.addr_.s_addr; + } + + /// Compare addresses for ordering. + friend bool operator<(const address_v4& a1, const address_v4& a2) + { + return a1.to_uint() < a2.to_uint(); + } + + /// Compare addresses for ordering. + friend bool operator>(const address_v4& a1, const address_v4& a2) + { + return a1.to_uint() > a2.to_uint(); + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v4& a1, const address_v4& a2) + { + return a1.to_uint() <= a2.to_uint(); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v4& a1, const address_v4& a2) + { + return a1.to_uint() >= a2.to_uint(); + } + + /// Obtain an address object that represents any address. + static address_v4 any() + { + return address_v4(); + } + + /// Obtain an address object that represents the loopback address. + static address_v4 loopback() + { + return address_v4(0x7F000001); + } + + /// Obtain an address object that represents the broadcast address. + static address_v4 broadcast() + { + return address_v4(0xFFFFFFFF); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use network_v4 class.) Obtain an address object that + /// represents the broadcast address that corresponds to the specified + /// address and netmask. + ASIO_DECL static address_v4 broadcast( + const address_v4& addr, const address_v4& mask); + + /// (Deprecated: Use network_v4 class.) Obtain the netmask that corresponds + /// to the address, based on its address class. + ASIO_DECL static address_v4 netmask(const address_v4& addr); +#endif // !defined(ASIO_NO_DEPRECATED) + +private: + // The underlying IPv4 address. + asio::detail::in4_addr_type addr_; +}; + +/// Create an IPv4 address from raw bytes in network order. +/** + * @relates address_v4 + */ +inline address_v4 make_address_v4(const address_v4::bytes_type& bytes) +{ + return address_v4(bytes); +} + +/// Create an IPv4 address from an unsigned integer in host byte order. +/** + * @relates address_v4 + */ +inline address_v4 make_address_v4(address_v4::uint_type addr) +{ + return address_v4(addr); +} + +/// Create an IPv4 address from an IP address string in dotted decimal form. +/** + * @relates address_v4 + */ +ASIO_DECL address_v4 make_address_v4(const char* str); + +/// Create an IPv4 address from an IP address string in dotted decimal form. +/** + * @relates address_v4 + */ +ASIO_DECL address_v4 make_address_v4( + const char* str, asio::error_code& ec); + +/// Create an IPv4 address from an IP address string in dotted decimal form. +/** + * @relates address_v4 + */ +ASIO_DECL address_v4 make_address_v4(const std::string& str); + +/// Create an IPv4 address from an IP address string in dotted decimal form. +/** + * @relates address_v4 + */ +ASIO_DECL address_v4 make_address_v4( + const std::string& str, asio::error_code& ec); + +#if defined(ASIO_HAS_STRING_VIEW) \ + || defined(GENERATING_DOCUMENTATION) + +/// Create an IPv4 address from an IP address string in dotted decimal form. +/** + * @relates address_v4 + */ +ASIO_DECL address_v4 make_address_v4(string_view str); + +/// Create an IPv4 address from an IP address string in dotted decimal form. +/** + * @relates address_v4 + */ +ASIO_DECL address_v4 make_address_v4( + string_view str, asio::error_code& ec); + +#endif // defined(ASIO_HAS_STRING_VIEW) + // || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_NO_IOSTREAM) + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v4 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v4& addr); + +#endif // !defined(ASIO_NO_IOSTREAM) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/impl/address_v4.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/address_v4.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_ADDRESS_V4_HPP diff --git a/subprojects/asio/include/asio/ip/address_v4_iterator.hpp b/subprojects/asio/include/asio/ip/address_v4_iterator.hpp new file mode 100644 index 000000000..e2ef3937e --- /dev/null +++ b/subprojects/asio/include/asio/ip/address_v4_iterator.hpp @@ -0,0 +1,162 @@ +// +// ip/address_v4_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V4_ITERATOR_HPP +#define ASIO_IP_ADDRESS_V4_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ip/address_v4.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template class basic_address_iterator; + +/// An input iterator that can be used for traversing IPv4 addresses. +/** + * In addition to satisfying the input iterator requirements, this iterator + * also supports decrement. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template <> class basic_address_iterator +{ +public: + /// The type of the elements pointed to by the iterator. + typedef address_v4 value_type; + + /// Distance between two iterators. + typedef std::ptrdiff_t difference_type; + + /// The type of a pointer to an element pointed to by the iterator. + typedef const address_v4* pointer; + + /// The type of a reference to an element pointed to by the iterator. + typedef const address_v4& reference; + + /// Denotes that the iterator satisfies the input iterator requirements. + typedef std::input_iterator_tag iterator_category; + + /// Construct an iterator that points to the specified address. + basic_address_iterator(const address_v4& addr) ASIO_NOEXCEPT + : address_(addr) + { + } + + /// Copy constructor. + basic_address_iterator( + const basic_address_iterator& other) ASIO_NOEXCEPT + : address_(other.address_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + basic_address_iterator(basic_address_iterator&& other) ASIO_NOEXCEPT + : address_(ASIO_MOVE_CAST(address_v4)(other.address_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assignment operator. + basic_address_iterator& operator=( + const basic_address_iterator& other) ASIO_NOEXCEPT + { + address_ = other.address_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move assignment operator. + basic_address_iterator& operator=( + basic_address_iterator&& other) ASIO_NOEXCEPT + { + address_ = ASIO_MOVE_CAST(address_v4)(other.address_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// Dereference the iterator. + const address_v4& operator*() const ASIO_NOEXCEPT + { + return address_; + } + + /// Dereference the iterator. + const address_v4* operator->() const ASIO_NOEXCEPT + { + return &address_; + } + + /// Pre-increment operator. + basic_address_iterator& operator++() ASIO_NOEXCEPT + { + address_ = address_v4((address_.to_uint() + 1) & 0xFFFFFFFF); + return *this; + } + + /// Post-increment operator. + basic_address_iterator operator++(int) ASIO_NOEXCEPT + { + basic_address_iterator tmp(*this); + ++*this; + return tmp; + } + + /// Pre-decrement operator. + basic_address_iterator& operator--() ASIO_NOEXCEPT + { + address_ = address_v4((address_.to_uint() - 1) & 0xFFFFFFFF); + return *this; + } + + /// Post-decrement operator. + basic_address_iterator operator--(int) + { + basic_address_iterator tmp(*this); + --*this; + return tmp; + } + + /// Compare two addresses for equality. + friend bool operator==(const basic_address_iterator& a, + const basic_address_iterator& b) + { + return a.address_ == b.address_; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const basic_address_iterator& a, + const basic_address_iterator& b) + { + return a.address_ != b.address_; + } + +private: + address_v4 address_; +}; + +/// An input iterator that can be used for traversing IPv4 addresses. +typedef basic_address_iterator address_v4_iterator; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V4_ITERATOR_HPP diff --git a/subprojects/asio/include/asio/ip/address_v4_range.hpp b/subprojects/asio/include/asio/ip/address_v4_range.hpp new file mode 100644 index 000000000..a40284208 --- /dev/null +++ b/subprojects/asio/include/asio/ip/address_v4_range.hpp @@ -0,0 +1,134 @@ +// +// ip/address_v4_range.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V4_RANGE_HPP +#define ASIO_IP_ADDRESS_V4_RANGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ip/address_v4_iterator.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template class basic_address_range; + +/// Represents a range of IPv4 addresses. +/** + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template <> class basic_address_range +{ +public: + /// The type of an iterator that points into the range. + typedef basic_address_iterator iterator; + + /// Construct an empty range. + basic_address_range() ASIO_NOEXCEPT + : begin_(address_v4()), + end_(address_v4()) + { + } + + /// Construct an range that represents the given range of addresses. + explicit basic_address_range(const iterator& first, + const iterator& last) ASIO_NOEXCEPT + : begin_(first), + end_(last) + { + } + + /// Copy constructor. + basic_address_range(const basic_address_range& other) ASIO_NOEXCEPT + : begin_(other.begin_), + end_(other.end_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + basic_address_range(basic_address_range&& other) ASIO_NOEXCEPT + : begin_(ASIO_MOVE_CAST(iterator)(other.begin_)), + end_(ASIO_MOVE_CAST(iterator)(other.end_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assignment operator. + basic_address_range& operator=( + const basic_address_range& other) ASIO_NOEXCEPT + { + begin_ = other.begin_; + end_ = other.end_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move assignment operator. + basic_address_range& operator=( + basic_address_range&& other) ASIO_NOEXCEPT + { + begin_ = ASIO_MOVE_CAST(iterator)(other.begin_); + end_ = ASIO_MOVE_CAST(iterator)(other.end_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// Obtain an iterator that points to the start of the range. + iterator begin() const ASIO_NOEXCEPT + { + return begin_; + } + + /// Obtain an iterator that points to the end of the range. + iterator end() const ASIO_NOEXCEPT + { + return end_; + } + + /// Determine whether the range is empty. + bool empty() const ASIO_NOEXCEPT + { + return size() == 0; + } + + /// Return the size of the range. + std::size_t size() const ASIO_NOEXCEPT + { + return end_->to_uint() - begin_->to_uint(); + } + + /// Find an address in the range. + iterator find(const address_v4& addr) const ASIO_NOEXCEPT + { + return addr >= *begin_ && addr < *end_ ? iterator(addr) : end_; + } + +private: + iterator begin_; + iterator end_; +}; + +/// Represents a range of IPv4 addresses. +typedef basic_address_range address_v4_range; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V4_RANGE_HPP diff --git a/subprojects/asio/include/asio/ip/address_v6.hpp b/subprojects/asio/include/asio/ip/address_v6.hpp new file mode 100644 index 000000000..fece33297 --- /dev/null +++ b/subprojects/asio/include/asio/ip/address_v6.hpp @@ -0,0 +1,336 @@ +// +// ip/address_v6.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V6_HPP +#define ASIO_IP_ADDRESS_V6_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/array.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/string_view.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/error_code.hpp" +#include "asio/ip/address_v4.hpp" + +#if !defined(ASIO_NO_IOSTREAM) +# include +#endif // !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template class basic_address_iterator; + +/// Implements IP version 6 style addresses. +/** + * The asio::ip::address_v6 class provides the ability to use and + * manipulate IP version 6 addresses. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address_v6 +{ +public: + /// The type used to represent an address as an array of bytes. + /** + * @note This type is defined in terms of the C++0x template @c std::array + * when it is available. Otherwise, it uses @c boost:array. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef array bytes_type; +#else + typedef asio::detail::array bytes_type; +#endif + + /// Default constructor. + ASIO_DECL address_v6(); + + /// Construct an address from raw bytes and scope ID. + ASIO_DECL explicit address_v6(const bytes_type& bytes, + unsigned long scope_id = 0); + + /// Copy constructor. + ASIO_DECL address_v6(const address_v6& other); + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + ASIO_DECL address_v6(address_v6&& other); +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from another address. + ASIO_DECL address_v6& operator=(const address_v6& other); + +#if defined(ASIO_HAS_MOVE) + /// Move-assign from another address. + ASIO_DECL address_v6& operator=(address_v6&& other); +#endif // defined(ASIO_HAS_MOVE) + + /// The scope ID of the address. + /** + * Returns the scope ID associated with the IPv6 address. + */ + unsigned long scope_id() const + { + return scope_id_; + } + + /// The scope ID of the address. + /** + * Modifies the scope ID associated with the IPv6 address. + */ + void scope_id(unsigned long id) + { + scope_id_ = id; + } + + /// Get the address in bytes, in network byte order. + ASIO_DECL bytes_type to_bytes() const; + + /// Get the address as a string. + ASIO_DECL std::string to_string() const; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use other overload.) Get the address as a string. + ASIO_DECL std::string to_string(asio::error_code& ec) const; + + /// (Deprecated: Use make_address_v6().) Create an IPv6 address from an IP + /// address string. + static address_v6 from_string(const char* str); + + /// (Deprecated: Use make_address_v6().) Create an IPv6 address from an IP + /// address string. + static address_v6 from_string( + const char* str, asio::error_code& ec); + + /// (Deprecated: Use make_address_v6().) Create an IPv6 address from an IP + /// address string. + static address_v6 from_string(const std::string& str); + + /// (Deprecated: Use make_address_v6().) Create an IPv6 address from an IP + /// address string. + static address_v6 from_string( + const std::string& str, asio::error_code& ec); + + /// (Deprecated: Use make_address_v4().) Converts an IPv4-mapped or + /// IPv4-compatible address to an IPv4 address. + ASIO_DECL address_v4 to_v4() const; +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Determine whether the address is a loopback address. + ASIO_DECL bool is_loopback() const; + + /// Determine whether the address is unspecified. + ASIO_DECL bool is_unspecified() const; + + /// Determine whether the address is link local. + ASIO_DECL bool is_link_local() const; + + /// Determine whether the address is site local. + ASIO_DECL bool is_site_local() const; + + /// Determine whether the address is a mapped IPv4 address. + ASIO_DECL bool is_v4_mapped() const; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: No replacement.) Determine whether the address is an + /// IPv4-compatible address. + ASIO_DECL bool is_v4_compatible() const; +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Determine whether the address is a multicast address. + ASIO_DECL bool is_multicast() const; + + /// Determine whether the address is a global multicast address. + ASIO_DECL bool is_multicast_global() const; + + /// Determine whether the address is a link-local multicast address. + ASIO_DECL bool is_multicast_link_local() const; + + /// Determine whether the address is a node-local multicast address. + ASIO_DECL bool is_multicast_node_local() const; + + /// Determine whether the address is a org-local multicast address. + ASIO_DECL bool is_multicast_org_local() const; + + /// Determine whether the address is a site-local multicast address. + ASIO_DECL bool is_multicast_site_local() const; + + /// Compare two addresses for equality. + ASIO_DECL friend bool operator==( + const address_v6& a1, const address_v6& a2); + + /// Compare two addresses for inequality. + friend bool operator!=(const address_v6& a1, const address_v6& a2) + { + return !(a1 == a2); + } + + /// Compare addresses for ordering. + ASIO_DECL friend bool operator<( + const address_v6& a1, const address_v6& a2); + + /// Compare addresses for ordering. + friend bool operator>(const address_v6& a1, const address_v6& a2) + { + return a2 < a1; + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v6& a1, const address_v6& a2) + { + return !(a2 < a1); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v6& a1, const address_v6& a2) + { + return !(a1 < a2); + } + + /// Obtain an address object that represents any address. + static address_v6 any() + { + return address_v6(); + } + + /// Obtain an address object that represents the loopback address. + ASIO_DECL static address_v6 loopback(); + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use make_address_v6().) Create an IPv4-mapped IPv6 address. + ASIO_DECL static address_v6 v4_mapped(const address_v4& addr); + + /// (Deprecated: No replacement.) Create an IPv4-compatible IPv6 address. + ASIO_DECL static address_v6 v4_compatible(const address_v4& addr); +#endif // !defined(ASIO_NO_DEPRECATED) + +private: + friend class basic_address_iterator; + + // The underlying IPv6 address. + asio::detail::in6_addr_type addr_; + + // The scope ID associated with the address. + unsigned long scope_id_; +}; + +/// Create an IPv6 address from raw bytes and scope ID. +/** + * @relates address_v6 + */ +inline address_v6 make_address_v6(const address_v6::bytes_type& bytes, + unsigned long scope_id = 0) +{ + return address_v6(bytes, scope_id); +} + +/// Create an IPv6 address from an IP address string. +/** + * @relates address_v6 + */ +ASIO_DECL address_v6 make_address_v6(const char* str); + +/// Create an IPv6 address from an IP address string. +/** + * @relates address_v6 + */ +ASIO_DECL address_v6 make_address_v6( + const char* str, asio::error_code& ec); + +/// Createan IPv6 address from an IP address string. +/** + * @relates address_v6 + */ +ASIO_DECL address_v6 make_address_v6(const std::string& str); + +/// Create an IPv6 address from an IP address string. +/** + * @relates address_v6 + */ +ASIO_DECL address_v6 make_address_v6( + const std::string& str, asio::error_code& ec); + +#if defined(ASIO_HAS_STRING_VIEW) \ + || defined(GENERATING_DOCUMENTATION) + +/// Create an IPv6 address from an IP address string. +/** + * @relates address_v6 + */ +ASIO_DECL address_v6 make_address_v6(string_view str); + +/// Create an IPv6 address from an IP address string. +/** + * @relates address_v6 + */ +ASIO_DECL address_v6 make_address_v6( + string_view str, asio::error_code& ec); + +#endif // defined(ASIO_HAS_STRING_VIEW) + // || defined(GENERATING_DOCUMENTATION) + +/// Tag type used for distinguishing overloads that deal in IPv4-mapped IPv6 +/// addresses. +enum v4_mapped_t { v4_mapped }; + +/// Create an IPv4 address from a IPv4-mapped IPv6 address. +/** + * @relates address_v4 + */ +ASIO_DECL address_v4 make_address_v4( + v4_mapped_t, const address_v6& v6_addr); + +/// Create an IPv4-mapped IPv6 address from an IPv4 address. +/** + * @relates address_v6 + */ +ASIO_DECL address_v6 make_address_v6( + v4_mapped_t, const address_v4& v4_addr); + +#if !defined(ASIO_NO_IOSTREAM) + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v6 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v6& addr); + +#endif // !defined(ASIO_NO_IOSTREAM) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/impl/address_v6.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/address_v6.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_ADDRESS_V6_HPP diff --git a/subprojects/asio/include/asio/ip/address_v6_iterator.hpp b/subprojects/asio/include/asio/ip/address_v6_iterator.hpp new file mode 100644 index 000000000..0a1fb3fea --- /dev/null +++ b/subprojects/asio/include/asio/ip/address_v6_iterator.hpp @@ -0,0 +1,183 @@ +// +// ip/address_v6_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Oliver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V6_ITERATOR_HPP +#define ASIO_IP_ADDRESS_V6_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ip/address_v6.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template class basic_address_iterator; + +/// An input iterator that can be used for traversing IPv6 addresses. +/** + * In addition to satisfying the input iterator requirements, this iterator + * also supports decrement. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template <> class basic_address_iterator +{ +public: + /// The type of the elements pointed to by the iterator. + typedef address_v6 value_type; + + /// Distance between two iterators. + typedef std::ptrdiff_t difference_type; + + /// The type of a pointer to an element pointed to by the iterator. + typedef const address_v6* pointer; + + /// The type of a reference to an element pointed to by the iterator. + typedef const address_v6& reference; + + /// Denotes that the iterator satisfies the input iterator requirements. + typedef std::input_iterator_tag iterator_category; + + /// Construct an iterator that points to the specified address. + basic_address_iterator(const address_v6& addr) ASIO_NOEXCEPT + : address_(addr) + { + } + + /// Copy constructor. + basic_address_iterator( + const basic_address_iterator& other) ASIO_NOEXCEPT + : address_(other.address_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + basic_address_iterator(basic_address_iterator&& other) ASIO_NOEXCEPT + : address_(ASIO_MOVE_CAST(address_v6)(other.address_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assignment operator. + basic_address_iterator& operator=( + const basic_address_iterator& other) ASIO_NOEXCEPT + { + address_ = other.address_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move assignment operator. + basic_address_iterator& operator=( + basic_address_iterator&& other) ASIO_NOEXCEPT + { + address_ = ASIO_MOVE_CAST(address_v6)(other.address_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// Dereference the iterator. + const address_v6& operator*() const ASIO_NOEXCEPT + { + return address_; + } + + /// Dereference the iterator. + const address_v6* operator->() const ASIO_NOEXCEPT + { + return &address_; + } + + /// Pre-increment operator. + basic_address_iterator& operator++() ASIO_NOEXCEPT + { + for (int i = 15; i >= 0; --i) + { + if (address_.addr_.s6_addr[i] < 0xFF) + { + ++address_.addr_.s6_addr[i]; + break; + } + + address_.addr_.s6_addr[i] = 0; + } + + return *this; + } + + /// Post-increment operator. + basic_address_iterator operator++(int) ASIO_NOEXCEPT + { + basic_address_iterator tmp(*this); + ++*this; + return tmp; + } + + /// Pre-decrement operator. + basic_address_iterator& operator--() ASIO_NOEXCEPT + { + for (int i = 15; i >= 0; --i) + { + if (address_.addr_.s6_addr[i] > 0) + { + --address_.addr_.s6_addr[i]; + break; + } + + address_.addr_.s6_addr[i] = 0xFF; + } + + return *this; + } + + /// Post-decrement operator. + basic_address_iterator operator--(int) + { + basic_address_iterator tmp(*this); + --*this; + return tmp; + } + + /// Compare two addresses for equality. + friend bool operator==(const basic_address_iterator& a, + const basic_address_iterator& b) + { + return a.address_ == b.address_; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const basic_address_iterator& a, + const basic_address_iterator& b) + { + return a.address_ != b.address_; + } + +private: + address_v6 address_; +}; + +/// An input iterator that can be used for traversing IPv6 addresses. +typedef basic_address_iterator address_v6_iterator; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V6_ITERATOR_HPP diff --git a/subprojects/asio/include/asio/ip/address_v6_range.hpp b/subprojects/asio/include/asio/ip/address_v6_range.hpp new file mode 100644 index 000000000..9d7062ef8 --- /dev/null +++ b/subprojects/asio/include/asio/ip/address_v6_range.hpp @@ -0,0 +1,129 @@ +// +// ip/address_v6_range.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Oliver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V6_RANGE_HPP +#define ASIO_IP_ADDRESS_V6_RANGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ip/address_v6_iterator.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template class basic_address_range; + +/// Represents a range of IPv6 addresses. +/** + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template <> class basic_address_range +{ +public: + /// The type of an iterator that points into the range. + typedef basic_address_iterator iterator; + + /// Construct an empty range. + basic_address_range() ASIO_NOEXCEPT + : begin_(address_v6()), + end_(address_v6()) + { + } + + /// Construct an range that represents the given range of addresses. + explicit basic_address_range(const iterator& first, + const iterator& last) ASIO_NOEXCEPT + : begin_(first), + end_(last) + { + } + + /// Copy constructor. + basic_address_range(const basic_address_range& other) ASIO_NOEXCEPT + : begin_(other.begin_), + end_(other.end_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + basic_address_range(basic_address_range&& other) ASIO_NOEXCEPT + : begin_(ASIO_MOVE_CAST(iterator)(other.begin_)), + end_(ASIO_MOVE_CAST(iterator)(other.end_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assignment operator. + basic_address_range& operator=( + const basic_address_range& other) ASIO_NOEXCEPT + { + begin_ = other.begin_; + end_ = other.end_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move assignment operator. + basic_address_range& operator=( + basic_address_range&& other) ASIO_NOEXCEPT + { + begin_ = ASIO_MOVE_CAST(iterator)(other.begin_); + end_ = ASIO_MOVE_CAST(iterator)(other.end_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// Obtain an iterator that points to the start of the range. + iterator begin() const ASIO_NOEXCEPT + { + return begin_; + } + + /// Obtain an iterator that points to the end of the range. + iterator end() const ASIO_NOEXCEPT + { + return end_; + } + + /// Determine whether the range is empty. + bool empty() const ASIO_NOEXCEPT + { + return begin_ == end_; + } + + /// Find an address in the range. + iterator find(const address_v6& addr) const ASIO_NOEXCEPT + { + return addr >= *begin_ && addr < *end_ ? iterator(addr) : end_; + } + +private: + iterator begin_; + iterator end_; +}; + +/// Represents a range of IPv6 addresses. +typedef basic_address_range address_v6_range; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V6_RANGE_HPP diff --git a/subprojects/asio/include/asio/ip/bad_address_cast.hpp b/subprojects/asio/include/asio/ip/bad_address_cast.hpp new file mode 100644 index 000000000..0838d4a71 --- /dev/null +++ b/subprojects/asio/include/asio/ip/bad_address_cast.hpp @@ -0,0 +1,53 @@ +// +// ip/bad_address_cast.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BAD_ADDRESS_CAST_HPP +#define ASIO_IP_BAD_ADDRESS_CAST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Thrown to indicate a failed address conversion. +class bad_address_cast : +#if defined(ASIO_MSVC) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS + public std::exception +#else + public std::bad_cast +#endif +{ +public: + /// Default constructor. + bad_address_cast() {} + + /// Destructor. + virtual ~bad_address_cast() ASIO_NOEXCEPT_OR_NOTHROW {} + + /// Get the message associated with the exception. + virtual const char* what() const ASIO_NOEXCEPT_OR_NOTHROW + { + return "bad address cast"; + } +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_HPP diff --git a/subprojects/asio/include/asio/ip/basic_endpoint.hpp b/subprojects/asio/include/asio/ip/basic_endpoint.hpp new file mode 100644 index 000000000..4418ee776 --- /dev/null +++ b/subprojects/asio/include/asio/ip/basic_endpoint.hpp @@ -0,0 +1,263 @@ +// +// ip/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_ENDPOINT_HPP +#define ASIO_IP_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ip/address.hpp" +#include "asio/ip/detail/endpoint.hpp" + +#if !defined(ASIO_NO_IOSTREAM) +# include +#endif // !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Describes an endpoint for a version-independent IP socket. +/** + * The asio::ip::basic_endpoint class template describes an endpoint that + * may be associated with a particular socket. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef InternetProtocol protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef asio::detail::socket_addr_type data_type; +#endif + + /// Default constructor. + basic_endpoint() + : impl_() + { + } + + /// Construct an endpoint using a port number, specified in the host's byte + /// order. The IP address will be the any address (i.e. INADDR_ANY or + /// in6addr_any). This constructor would typically be used for accepting new + /// connections. + /** + * @par Examples + * To initialise an IPv4 TCP endpoint for port 1234, use: + * @code + * asio::ip::tcp::endpoint ep(asio::ip::tcp::v4(), 1234); + * @endcode + * + * To specify an IPv6 UDP endpoint for port 9876, use: + * @code + * asio::ip::udp::endpoint ep(asio::ip::udp::v6(), 9876); + * @endcode + */ + basic_endpoint(const InternetProtocol& internet_protocol, + unsigned short port_num) + : impl_(internet_protocol.family(), port_num) + { + } + + /// Construct an endpoint using a port number and an IP address. This + /// constructor may be used for accepting connections on a specific interface + /// or for making a connection to a remote endpoint. + basic_endpoint(const asio::ip::address& addr, unsigned short port_num) + : impl_(addr, port_num) + { + } + + /// Copy constructor. + basic_endpoint(const basic_endpoint& other) + : impl_(other.impl_) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move constructor. + basic_endpoint(basic_endpoint&& other) + : impl_(other.impl_) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Assign from another endpoint. + basic_endpoint& operator=(const basic_endpoint& other) + { + impl_ = other.impl_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-assign from another endpoint. + basic_endpoint& operator=(basic_endpoint&& other) + { + impl_ = other.impl_; + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + if (impl_.is_v4()) + return InternetProtocol::v4(); + return InternetProtocol::v6(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return impl_.data(); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return impl_.data(); + } + + /// Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return impl_.size(); + } + + /// Set the underlying size of the endpoint in the native type. + void resize(std::size_t new_size) + { + impl_.resize(new_size); + } + + /// Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return impl_.capacity(); + } + + /// Get the port associated with the endpoint. The port number is always in + /// the host's byte order. + unsigned short port() const + { + return impl_.port(); + } + + /// Set the port associated with the endpoint. The port number is always in + /// the host's byte order. + void port(unsigned short port_num) + { + impl_.port(port_num); + } + + /// Get the IP address associated with the endpoint. + asio::ip::address address() const + { + return impl_.address(); + } + + /// Set the IP address associated with the endpoint. + void address(const asio::ip::address& addr) + { + impl_.address(addr); + } + + /// Compare two endpoints for equality. + friend bool operator==(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.impl_ == e2.impl_; + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1 == e2); + } + + /// Compare endpoints for ordering. + friend bool operator<(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.impl_ < e2.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator>(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e2.impl_ < e1.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator<=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e2 < e1); + } + + /// Compare endpoints for ordering. + friend bool operator>=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1 < e2); + } + +private: + // The underlying IP endpoint. + asio::ip::detail::endpoint impl_; +}; + +#if !defined(ASIO_NO_IOSTREAM) + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates asio::ip::basic_endpoint + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint); + +#endif // !defined(ASIO_NO_IOSTREAM) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/impl/basic_endpoint.hpp" + +#endif // ASIO_IP_BASIC_ENDPOINT_HPP diff --git a/subprojects/asio/include/asio/ip/basic_resolver.hpp b/subprojects/asio/include/asio/ip/basic_resolver.hpp new file mode 100644 index 000000000..9f4089568 --- /dev/null +++ b/subprojects/asio/include/asio/ip/basic_resolver.hpp @@ -0,0 +1,1020 @@ +// +// ip/basic_resolver.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_HPP +#define ASIO_IP_BASIC_RESOLVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/string_view.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/ip/resolver_base.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/ip/resolver_service.hpp" +#else // defined(ASIO_ENABLE_OLD_SERVICES) +# if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_resolver_service.hpp" +# define ASIO_SVC_T \ + asio::detail::winrt_resolver_service +# else +# include "asio/detail/resolver_service.hpp" +# define ASIO_SVC_T \ + asio::detail::resolver_service +# endif +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Provides endpoint resolution functionality. +/** + * The basic_resolver class template provides the ability to resolve a query + * to a list of endpoints. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template )> +class basic_resolver + : ASIO_SVC_ACCESS basic_io_object, + public resolver_base +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The protocol type. + typedef InternetProtocol protocol_type; + + /// The endpoint type. + typedef typename InternetProtocol::endpoint endpoint_type; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated.) The query type. + typedef basic_resolver_query query; + + /// (Deprecated.) The iterator type. + typedef basic_resolver_iterator iterator; +#endif // !defined(ASIO_NO_DEPRECATED) + + /// The results type. + typedef basic_resolver_results results_type; + + /// Constructor. + /** + * This constructor creates a basic_resolver. + * + * @param io_context The io_context object that the resolver will use to + * dispatch handlers for any asynchronous operations performed on the + * resolver. + */ + explicit basic_resolver(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_resolver from another. + /** + * This constructor moves a resolver from one object to another. + * + * @param other The other basic_resolver object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_resolver(io_context&) constructor. + */ + basic_resolver(basic_resolver&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign a basic_resolver from another. + /** + * This assignment operator moves a resolver from one object to another. + * Cancels any outstanding asynchronous operations associated with the target + * object. + * + * @param other The other basic_resolver object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_resolver(io_context&) constructor. + */ + basic_resolver& operator=(basic_resolver&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the resolver. + /** + * This function destroys the resolver, cancelling any outstanding + * asynchronous wait operations associated with the resolver as if by calling + * @c cancel. + */ + ~basic_resolver() + { + } + +#if defined(ASIO_ENABLE_OLD_SERVICES) + // These functions are provided by basic_io_object<>. +#else // defined(ASIO_ENABLE_OLD_SERVICES) +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + + /// Cancel any asynchronous operations that are waiting on the resolver. + /** + * This function forces the completion of any pending asynchronous + * operations on the host resolver. The handler for each cancelled operation + * will be invoked with the asio::error::operation_aborted error code. + */ + void cancel() + { + return this->get_service().cancel(this->get_implementation()); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use overload with separate host and service parameters.) + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @returns A range object representing the list of endpoint entries. A + * successful call to this function is guaranteed to return a non-empty + * range. + * + * @throws asio::system_error Thrown on failure. + */ + results_type resolve(const query& q) + { + asio::error_code ec; + results_type r = this->get_service().resolve( + this->get_implementation(), q, ec); + asio::detail::throw_error(ec, "resolve"); + return r; + } + + /// (Deprecated: Use overload with separate host and service parameters.) + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A range object representing the list of endpoint entries. An + * empty range is returned if an error occurs. A successful call to this + * function is guaranteed to return a non-empty range. + */ + results_type resolve(const query& q, asio::error_code& ec) + { + return this->get_service().resolve(this->get_implementation(), q, ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @returns A range object representing the list of endpoint entries. A + * successful call to this function is guaranteed to return a non-empty + * range. + * + * @throws asio::system_error Thrown on failure. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(ASIO_STRING_VIEW_PARAM host, + ASIO_STRING_VIEW_PARAM service) + { + return resolve(host, service, resolver_base::flags()); + } + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A range object representing the list of endpoint entries. An + * empty range is returned if an error occurs. A successful call to this + * function is guaranteed to return a non-empty range. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(ASIO_STRING_VIEW_PARAM host, + ASIO_STRING_VIEW_PARAM service, asio::error_code& ec) + { + return resolve(host, service, resolver_base::flags(), ec); + } + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @returns A range object representing the list of endpoint entries. A + * successful call to this function is guaranteed to return a non-empty + * range. + * + * @throws asio::system_error Thrown on failure. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(ASIO_STRING_VIEW_PARAM host, + ASIO_STRING_VIEW_PARAM service, resolver_base::flags resolve_flags) + { + asio::error_code ec; + basic_resolver_query q(static_cast(host), + static_cast(service), resolve_flags); + results_type r = this->get_service().resolve( + this->get_implementation(), q, ec); + asio::detail::throw_error(ec, "resolve"); + return r; + } + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A range object representing the list of endpoint entries. An + * empty range is returned if an error occurs. A successful call to this + * function is guaranteed to return a non-empty range. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(ASIO_STRING_VIEW_PARAM host, + ASIO_STRING_VIEW_PARAM service, resolver_base::flags resolve_flags, + asio::error_code& ec) + { + basic_resolver_query q(static_cast(host), + static_cast(service), resolve_flags); + return this->get_service().resolve(this->get_implementation(), q, ec); + } + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @returns A range object representing the list of endpoint entries. A + * successful call to this function is guaranteed to return a non-empty + * range. + * + * @throws asio::system_error Thrown on failure. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(const protocol_type& protocol, + ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service) + { + return resolve(protocol, host, service, resolver_base::flags()); + } + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A range object representing the list of endpoint entries. An + * empty range is returned if an error occurs. A successful call to this + * function is guaranteed to return a non-empty range. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(const protocol_type& protocol, + ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service, + asio::error_code& ec) + { + return resolve(protocol, host, service, resolver_base::flags(), ec); + } + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @returns A range object representing the list of endpoint entries. A + * successful call to this function is guaranteed to return a non-empty + * range. + * + * @throws asio::system_error Thrown on failure. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(const protocol_type& protocol, + ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service, + resolver_base::flags resolve_flags) + { + asio::error_code ec; + basic_resolver_query q( + protocol, static_cast(host), + static_cast(service), resolve_flags); + results_type r = this->get_service().resolve( + this->get_implementation(), q, ec); + asio::detail::throw_error(ec, "resolve"); + return r; + } + + /// Perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A range object representing the list of endpoint entries. An + * empty range is returned if an error occurs. A successful call to this + * function is guaranteed to return a non-empty range. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + results_type resolve(const protocol_type& protocol, + ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service, + resolver_base::flags resolve_flags, asio::error_code& ec) + { + basic_resolver_query q( + protocol, static_cast(host), + static_cast(service), resolve_flags); + return this->get_service().resolve(this->get_implementation(), q, ec); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use overload with separate host and service parameters.) + /// Asynchronously perform forward resolution of a query to a list of entries. + /** + * This function is used to asynchronously resolve a query into a list of + * endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::results_type results // Resolved endpoints as a range. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * A successful resolve operation is guaranteed to pass a non-empty range to + * the handler. + */ + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(const query& q, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ResolveHandler. + ASIO_RESOLVE_HANDLER_CHECK( + ResolveHandler, handler, results_type) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_resolve(this->get_implementation(), q, + ASIO_MOVE_CAST(ResolveHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + asio::async_completion init(handler); + + this->get_service().async_resolve( + this->get_implementation(), q, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Asynchronously perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::results_type results // Resolved endpoints as a range. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * A successful resolve operation is guaranteed to pass a non-empty range to + * the handler. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(ASIO_STRING_VIEW_PARAM host, + ASIO_STRING_VIEW_PARAM service, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + return async_resolve(host, service, resolver_base::flags(), + ASIO_MOVE_CAST(ResolveHandler)(handler)); + } + + /// Asynchronously perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::results_type results // Resolved endpoints as a range. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * A successful resolve operation is guaranteed to pass a non-empty range to + * the handler. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(ASIO_STRING_VIEW_PARAM host, + ASIO_STRING_VIEW_PARAM service, + resolver_base::flags resolve_flags, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ResolveHandler. + ASIO_RESOLVE_HANDLER_CHECK( + ResolveHandler, handler, results_type) type_check; + + basic_resolver_query q(static_cast(host), + static_cast(service), resolve_flags); + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_resolve(this->get_implementation(), q, + ASIO_MOVE_CAST(ResolveHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + asio::async_completion init(handler); + + this->get_service().async_resolve( + this->get_implementation(), q, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Asynchronously perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::results_type results // Resolved endpoints as a range. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * A successful resolve operation is guaranteed to pass a non-empty range to + * the handler. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(const protocol_type& protocol, + ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + return async_resolve(protocol, host, service, resolver_base::flags(), + ASIO_MOVE_CAST(ResolveHandler)(handler)); + } + + /// Asynchronously perform forward resolution of a query to a list of entries. + /** + * This function is used to resolve host and service names into a list of + * endpoint entries. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::results_type results // Resolved endpoints as a range. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * A successful resolve operation is guaranteed to pass a non-empty range to + * the handler. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(const protocol_type& protocol, + ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service, + resolver_base::flags resolve_flags, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ResolveHandler. + ASIO_RESOLVE_HANDLER_CHECK( + ResolveHandler, handler, results_type) type_check; + + basic_resolver_query q( + protocol, static_cast(host), + static_cast(service), resolve_flags); + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_resolve(this->get_implementation(), q, + ASIO_MOVE_CAST(ResolveHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + asio::async_completion init(handler); + + this->get_service().async_resolve( + this->get_implementation(), q, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } + + /// Perform reverse resolution of an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @returns A range object representing the list of endpoint entries. A + * successful call to this function is guaranteed to return a non-empty + * range. + * + * @throws asio::system_error Thrown on failure. + */ + results_type resolve(const endpoint_type& e) + { + asio::error_code ec; + results_type i = this->get_service().resolve( + this->get_implementation(), e, ec); + asio::detail::throw_error(ec, "resolve"); + return i; + } + + /// Perform reverse resolution of an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A range object representing the list of endpoint entries. An + * empty range is returned if an error occurs. A successful call to this + * function is guaranteed to return a non-empty range. + */ + results_type resolve(const endpoint_type& e, asio::error_code& ec) + { + return this->get_service().resolve(this->get_implementation(), e, ec); + } + + /// Asynchronously perform reverse resolution of an endpoint to a list of + /// entries. + /** + * This function is used to asynchronously resolve an endpoint into a list of + * endpoint entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::results_type results // Resolved endpoints as a range. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * A successful resolve operation is guaranteed to pass a non-empty range to + * the handler. + */ + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(const endpoint_type& e, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ResolveHandler. + ASIO_RESOLVE_HANDLER_CHECK( + ResolveHandler, handler, results_type) type_check; + +#if defined(ASIO_ENABLE_OLD_SERVICES) + return this->get_service().async_resolve(this->get_implementation(), e, + ASIO_MOVE_CAST(ResolveHandler)(handler)); +#else // defined(ASIO_ENABLE_OLD_SERVICES) + asio::async_completion init(handler); + + this->get_service().async_resolve( + this->get_implementation(), e, init.completion_handler); + + return init.result.get(); +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + } +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# undef ASIO_SVC_T +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_IP_BASIC_RESOLVER_HPP diff --git a/subprojects/asio/include/asio/ip/basic_resolver_entry.hpp b/subprojects/asio/include/asio/ip/basic_resolver_entry.hpp new file mode 100644 index 000000000..99bcbd29e --- /dev/null +++ b/subprojects/asio/include/asio/ip/basic_resolver_entry.hpp @@ -0,0 +1,113 @@ +// +// ip/basic_resolver_entry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_ENTRY_HPP +#define ASIO_IP_BASIC_RESOLVER_ENTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/string_view.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// An entry produced by a resolver. +/** + * The asio::ip::basic_resolver_entry class template describes an entry + * as returned by a resolver. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_entry +{ +public: + /// The protocol type associated with the endpoint entry. + typedef InternetProtocol protocol_type; + + /// The endpoint type associated with the endpoint entry. + typedef typename InternetProtocol::endpoint endpoint_type; + + /// Default constructor. + basic_resolver_entry() + { + } + + /// Construct with specified endpoint, host name and service name. + basic_resolver_entry(const endpoint_type& ep, + ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service) + : endpoint_(ep), + host_name_(static_cast(host)), + service_name_(static_cast(service)) + { + } + + /// Get the endpoint associated with the entry. + endpoint_type endpoint() const + { + return endpoint_; + } + + /// Convert to the endpoint associated with the entry. + operator endpoint_type() const + { + return endpoint_; + } + + /// Get the host name associated with the entry. + std::string host_name() const + { + return host_name_; + } + + /// Get the host name associated with the entry. + template + std::basic_string, Allocator> host_name( + const Allocator& alloc = Allocator()) const + { + return std::basic_string, Allocator>( + host_name_.c_str(), alloc); + } + + /// Get the service name associated with the entry. + std::string service_name() const + { + return service_name_; + } + + /// Get the service name associated with the entry. + template + std::basic_string, Allocator> service_name( + const Allocator& alloc = Allocator()) const + { + return std::basic_string, Allocator>( + service_name_.c_str(), alloc); + } + +private: + endpoint_type endpoint_; + std::string host_name_; + std::string service_name_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_ENTRY_HPP diff --git a/subprojects/asio/include/asio/ip/basic_resolver_iterator.hpp b/subprojects/asio/include/asio/ip/basic_resolver_iterator.hpp new file mode 100644 index 000000000..ec5412cf6 --- /dev/null +++ b/subprojects/asio/include/asio/ip/basic_resolver_iterator.hpp @@ -0,0 +1,192 @@ +// +// ip/basic_resolver_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP +#define ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/ip/basic_resolver_entry.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_utils.hpp" +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// An iterator over the entries produced by a resolver. +/** + * The asio::ip::basic_resolver_iterator class template is used to define + * iterators over the results returned by a resolver. + * + * The iterator's value_type, obtained when the iterator is dereferenced, is: + * @code const basic_resolver_entry @endcode + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_iterator +{ +public: + /// The type used for the distance between two iterators. + typedef std::ptrdiff_t difference_type; + + /// The type of the value pointed to by the iterator. + typedef basic_resolver_entry value_type; + + /// The type of the result of applying operator->() to the iterator. + typedef const basic_resolver_entry* pointer; + + /// The type of the result of applying operator*() to the iterator. + typedef const basic_resolver_entry& reference; + + /// The iterator category. + typedef std::forward_iterator_tag iterator_category; + + /// Default constructor creates an end iterator. + basic_resolver_iterator() + : index_(0) + { + } + + /// Copy constructor. + basic_resolver_iterator(const basic_resolver_iterator& other) + : values_(other.values_), + index_(other.index_) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move constructor. + basic_resolver_iterator(basic_resolver_iterator&& other) + : values_(ASIO_MOVE_CAST(values_ptr_type)(other.values_)), + index_(other.index_) + { + other.index_ = 0; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Assignment operator. + basic_resolver_iterator& operator=(const basic_resolver_iterator& other) + { + values_ = other.values_; + index_ = other.index_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-assignment operator. + basic_resolver_iterator& operator=(basic_resolver_iterator&& other) + { + if (this != &other) + { + values_ = ASIO_MOVE_CAST(values_ptr_type)(other.values_); + index_ = other.index_; + other.index_ = 0; + } + + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Dereference an iterator. + const basic_resolver_entry& operator*() const + { + return dereference(); + } + + /// Dereference an iterator. + const basic_resolver_entry* operator->() const + { + return &dereference(); + } + + /// Increment operator (prefix). + basic_resolver_iterator& operator++() + { + increment(); + return *this; + } + + /// Increment operator (postfix). + basic_resolver_iterator operator++(int) + { + basic_resolver_iterator tmp(*this); + ++*this; + return tmp; + } + + /// Test two iterators for equality. + friend bool operator==(const basic_resolver_iterator& a, + const basic_resolver_iterator& b) + { + return a.equal(b); + } + + /// Test two iterators for inequality. + friend bool operator!=(const basic_resolver_iterator& a, + const basic_resolver_iterator& b) + { + return !a.equal(b); + } + +protected: + void increment() + { + if (++index_ == values_->size()) + { + // Reset state to match a default constructed end iterator. + values_.reset(); + index_ = 0; + } + } + + bool equal(const basic_resolver_iterator& other) const + { + if (!values_ && !other.values_) + return true; + if (values_ != other.values_) + return false; + return index_ == other.index_; + } + + const basic_resolver_entry& dereference() const + { + return (*values_)[index_]; + } + + typedef std::vector > values_type; + typedef asio::detail::shared_ptr values_ptr_type; + values_ptr_type values_; + std::size_t index_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP diff --git a/subprojects/asio/include/asio/ip/basic_resolver_query.hpp b/subprojects/asio/include/asio/ip/basic_resolver_query.hpp new file mode 100644 index 000000000..84cd98d55 --- /dev/null +++ b/subprojects/asio/include/asio/ip/basic_resolver_query.hpp @@ -0,0 +1,244 @@ +// +// ip/basic_resolver_query.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_QUERY_HPP +#define ASIO_IP_BASIC_RESOLVER_QUERY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/ip/resolver_query_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// An query to be passed to a resolver. +/** + * The asio::ip::basic_resolver_query class template describes a query + * that can be passed to a resolver. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_query + : public resolver_query_base +{ +public: + /// The protocol type associated with the endpoint query. + typedef InternetProtocol protocol_type; + + /// Construct with specified service name for any protocol. + /** + * This constructor is typically used to perform name resolution for local + * service binding. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for local service + * binding. + * + * @note On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + basic_resolver_query(const std::string& service, + resolver_query_base::flags resolve_flags = passive | address_configured) + : hints_(), + host_name_(), + service_name_(service) + { + typename InternetProtocol::endpoint endpoint; + hints_.ai_flags = static_cast(resolve_flags); + hints_.ai_family = PF_UNSPEC; + hints_.ai_socktype = endpoint.protocol().type(); + hints_.ai_protocol = endpoint.protocol().protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified service name for a given protocol. + /** + * This constructor is typically used to perform name resolution for local + * service binding with a specific protocol version. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for local service + * binding. + * + * @note On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + basic_resolver_query(const protocol_type& protocol, + const std::string& service, + resolver_query_base::flags resolve_flags = passive | address_configured) + : hints_(), + host_name_(), + service_name_(service) + { + hints_.ai_flags = static_cast(resolve_flags); + hints_.ai_family = protocol.family(); + hints_.ai_socktype = protocol.type(); + hints_.ai_protocol = protocol.protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified host name and service name for any protocol. + /** + * This constructor is typically used to perform name resolution for + * communication with remote hosts. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + basic_resolver_query(const std::string& host, const std::string& service, + resolver_query_base::flags resolve_flags = address_configured) + : hints_(), + host_name_(host), + service_name_(service) + { + typename InternetProtocol::endpoint endpoint; + hints_.ai_flags = static_cast(resolve_flags); + hints_.ai_family = ASIO_OS_DEF(AF_UNSPEC); + hints_.ai_socktype = endpoint.protocol().type(); + hints_.ai_protocol = endpoint.protocol().protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified host name and service name for a given protocol. + /** + * This constructor is typically used to perform name resolution for + * communication with remote hosts. + * + * @param protocol A protocol object, normally representing either the IPv4 or + * IPv6 version of an internet protocol. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. If an empty string and the passive flag has been + * specified, the resolved endpoints are suitable for local service binding. + * If an empty string and passive is not specified, the resolved endpoints + * will use the loopback address. + * + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. May + * be an empty string, in which case all resolved endpoints will have a port + * number of 0. + * + * @param resolve_flags A set of flags that determine how name resolution + * should be performed. The default flags are suitable for communication with + * remote hosts. + * + * @note On POSIX systems, host names may be locally defined in the file + * /etc/hosts. On Windows, host names may be defined in the file + * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name + * resolution is performed using DNS. Operating systems may use additional + * locations when resolving host names (such as NETBIOS names on Windows). + * + * On POSIX systems, service names are typically defined in the file + * /etc/services. On Windows, service names may be found in the file + * c:\\windows\\system32\\drivers\\etc\\services. Operating systems + * may use additional locations when resolving service names. + */ + basic_resolver_query(const protocol_type& protocol, + const std::string& host, const std::string& service, + resolver_query_base::flags resolve_flags = address_configured) + : hints_(), + host_name_(host), + service_name_(service) + { + hints_.ai_flags = static_cast(resolve_flags); + hints_.ai_family = protocol.family(); + hints_.ai_socktype = protocol.type(); + hints_.ai_protocol = protocol.protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Get the hints associated with the query. + const asio::detail::addrinfo_type& hints() const + { + return hints_; + } + + /// Get the host name associated with the query. + std::string host_name() const + { + return host_name_; + } + + /// Get the service name associated with the query. + std::string service_name() const + { + return service_name_; + } + +private: + asio::detail::addrinfo_type hints_; + std::string host_name_; + std::string service_name_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_QUERY_HPP diff --git a/subprojects/asio/include/asio/ip/basic_resolver_results.hpp b/subprojects/asio/include/asio/ip/basic_resolver_results.hpp new file mode 100644 index 000000000..b87e33655 --- /dev/null +++ b/subprojects/asio/include/asio/ip/basic_resolver_results.hpp @@ -0,0 +1,311 @@ +// +// ip/basic_resolver_results.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_RESULTS_HPP +#define ASIO_IP_BASIC_RESOLVER_RESULTS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_utils.hpp" +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// A range of entries produced by a resolver. +/** + * The asio::ip::basic_resolver_results class template is used to define + * a range over the results returned by a resolver. + * + * The iterator's value_type, obtained when a results iterator is dereferenced, + * is: @code const basic_resolver_entry @endcode + * + * @note For backward compatibility, basic_resolver_results is derived from + * basic_resolver_iterator. This derivation is deprecated. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_results +#if !defined(ASIO_NO_DEPRECATED) + : public basic_resolver_iterator +#else // !defined(ASIO_NO_DEPRECATED) + : private basic_resolver_iterator +#endif // !defined(ASIO_NO_DEPRECATED) +{ +public: + /// The protocol type associated with the results. + typedef InternetProtocol protocol_type; + + /// The endpoint type associated with the results. + typedef typename protocol_type::endpoint endpoint_type; + + /// The type of a value in the results range. + typedef basic_resolver_entry value_type; + + /// The type of a const reference to a value in the range. + typedef const value_type& const_reference; + + /// The type of a non-const reference to a value in the range. + typedef value_type& reference; + + /// The type of an iterator into the range. + typedef basic_resolver_iterator const_iterator; + + /// The type of an iterator into the range. + typedef const_iterator iterator; + + /// Type used to represent the distance between two iterators in the range. + typedef std::ptrdiff_t difference_type; + + /// Type used to represent a count of the elements in the range. + typedef std::size_t size_type; + + /// Default constructor creates an empty range. + basic_resolver_results() + { + } + + /// Copy constructor. + basic_resolver_results(const basic_resolver_results& other) + : basic_resolver_iterator(other) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move constructor. + basic_resolver_results(basic_resolver_results&& other) + : basic_resolver_iterator( + ASIO_MOVE_CAST(basic_resolver_results)(other)) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Assignment operator. + basic_resolver_results& operator=(const basic_resolver_results& other) + { + basic_resolver_iterator::operator=(other); + return *this; + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-assignment operator. + basic_resolver_results& operator=(basic_resolver_results&& other) + { + basic_resolver_iterator::operator=( + ASIO_MOVE_CAST(basic_resolver_results)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +#if !defined(GENERATING_DOCUMENTATION) + // Create results from an addrinfo list returned by getaddrinfo. + static basic_resolver_results create( + asio::detail::addrinfo_type* address_info, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_results results; + if (!address_info) + return results; + + std::string actual_host_name = host_name; + if (address_info->ai_canonname) + actual_host_name = address_info->ai_canonname; + + results.values_.reset(new values_type); + + while (address_info) + { + if (address_info->ai_family == ASIO_OS_DEF(AF_INET) + || address_info->ai_family == ASIO_OS_DEF(AF_INET6)) + { + using namespace std; // For memcpy. + typename InternetProtocol::endpoint endpoint; + endpoint.resize(static_cast(address_info->ai_addrlen)); + memcpy(endpoint.data(), address_info->ai_addr, + address_info->ai_addrlen); + results.values_->push_back( + basic_resolver_entry(endpoint, + actual_host_name, service_name)); + } + address_info = address_info->ai_next; + } + + return results; + } + + // Create results from an endpoint, host name and service name. + static basic_resolver_results create(const endpoint_type& endpoint, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_results results; + results.values_.reset(new values_type); + results.values_->push_back( + basic_resolver_entry( + endpoint, host_name, service_name)); + return results; + } + + // Create results from a sequence of endpoints, host and service name. + template + static basic_resolver_results create( + EndpointIterator begin, EndpointIterator end, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_results results; + if (begin != end) + { + results.values_.reset(new values_type); + for (EndpointIterator ep_iter = begin; ep_iter != end; ++ep_iter) + { + results.values_->push_back( + basic_resolver_entry( + *ep_iter, host_name, service_name)); + } + } + return results; + } + +# if defined(ASIO_WINDOWS_RUNTIME) + // Create results from a Windows Runtime list of EndpointPair objects. + static basic_resolver_results create( + Windows::Foundation::Collections::IVectorView< + Windows::Networking::EndpointPair^>^ endpoints, + const asio::detail::addrinfo_type& hints, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_results results; + if (endpoints->Size) + { + results.values_.reset(new values_type); + for (unsigned int i = 0; i < endpoints->Size; ++i) + { + auto pair = endpoints->GetAt(i); + + if (hints.ai_family == ASIO_OS_DEF(AF_INET) + && pair->RemoteHostName->Type + != Windows::Networking::HostNameType::Ipv4) + continue; + + if (hints.ai_family == ASIO_OS_DEF(AF_INET6) + && pair->RemoteHostName->Type + != Windows::Networking::HostNameType::Ipv6) + continue; + + results.values_->push_back( + basic_resolver_entry( + typename InternetProtocol::endpoint( + ip::make_address( + asio::detail::winrt_utils::string( + pair->RemoteHostName->CanonicalName)), + asio::detail::winrt_utils::integer( + pair->RemoteServiceName)), + host_name, service_name)); + } + } + return results; + } +# endif // defined(ASIO_WINDOWS_RUNTIME) +#endif // !defined(GENERATING_DOCUMENTATION) + + /// Get the number of entries in the results range. + size_type size() const ASIO_NOEXCEPT + { + return this->values_ ? this->values_->size() : 0; + } + + /// Get the maximum number of entries permitted in a results range. + size_type max_size() const ASIO_NOEXCEPT + { + return this->values_ ? this->values_->max_size() : values_type().max_size(); + } + + /// Determine whether the results range is empty. + bool empty() const ASIO_NOEXCEPT + { + return this->values_ ? this->values_->empty() : true; + } + + /// Obtain a begin iterator for the results range. + const_iterator begin() const + { + basic_resolver_results tmp(*this); + tmp.index_ = 0; + return ASIO_MOVE_CAST(basic_resolver_results)(tmp); + } + + /// Obtain an end iterator for the results range. + const_iterator end() const + { + return const_iterator(); + } + + /// Obtain a begin iterator for the results range. + const_iterator cbegin() const + { + return begin(); + } + + /// Obtain an end iterator for the results range. + const_iterator cend() const + { + return end(); + } + + /// Swap the results range with another. + void swap(basic_resolver_results& that) ASIO_NOEXCEPT + { + if (this != &that) + { + this->values_.swap(that.values_); + std::size_t index = this->index_; + this->index_ = that.index_; + that.index_ = index; + } + } + + /// Test two iterators for equality. + friend bool operator==(const basic_resolver_results& a, + const basic_resolver_results& b) + { + return a.equal(b); + } + + /// Test two iterators for inequality. + friend bool operator!=(const basic_resolver_results& a, + const basic_resolver_results& b) + { + return !a.equal(b); + } + +private: + typedef std::vector > values_type; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_RESULTS_HPP diff --git a/subprojects/asio/include/asio/ip/detail/endpoint.hpp b/subprojects/asio/include/asio/ip/detail/endpoint.hpp new file mode 100644 index 000000000..9acefe5fe --- /dev/null +++ b/subprojects/asio/include/asio/ip/detail/endpoint.hpp @@ -0,0 +1,139 @@ +// +// ip/detail/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_DETAIL_ENDPOINT_HPP +#define ASIO_IP_DETAIL_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/error_code.hpp" +#include "asio/ip/address.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { +namespace detail { + +// Helper class for implementating an IP endpoint. +class endpoint +{ +public: + // Default constructor. + ASIO_DECL endpoint(); + + // Construct an endpoint using a family and port number. + ASIO_DECL endpoint(int family, unsigned short port_num); + + // Construct an endpoint using an address and port number. + ASIO_DECL endpoint(const asio::ip::address& addr, + unsigned short port_num); + + // Copy constructor. + endpoint(const endpoint& other) + : data_(other.data_) + { + } + + // Assign from another endpoint. + endpoint& operator=(const endpoint& other) + { + data_ = other.data_; + return *this; + } + + // Get the underlying endpoint in the native type. + asio::detail::socket_addr_type* data() + { + return &data_.base; + } + + // Get the underlying endpoint in the native type. + const asio::detail::socket_addr_type* data() const + { + return &data_.base; + } + + // Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + if (is_v4()) + return sizeof(asio::detail::sockaddr_in4_type); + else + return sizeof(asio::detail::sockaddr_in6_type); + } + + // Set the underlying size of the endpoint in the native type. + ASIO_DECL void resize(std::size_t new_size); + + // Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return sizeof(data_); + } + + // Get the port associated with the endpoint. + ASIO_DECL unsigned short port() const; + + // Set the port associated with the endpoint. + ASIO_DECL void port(unsigned short port_num); + + // Get the IP address associated with the endpoint. + ASIO_DECL asio::ip::address address() const; + + // Set the IP address associated with the endpoint. + ASIO_DECL void address(const asio::ip::address& addr); + + // Compare two endpoints for equality. + ASIO_DECL friend bool operator==( + const endpoint& e1, const endpoint& e2); + + // Compare endpoints for ordering. + ASIO_DECL friend bool operator<( + const endpoint& e1, const endpoint& e2); + + // Determine whether the endpoint is IPv4. + bool is_v4() const + { + return data_.base.sa_family == ASIO_OS_DEF(AF_INET); + } + +#if !defined(ASIO_NO_IOSTREAM) + // Convert to a string. + ASIO_DECL std::string to_string() const; +#endif // !defined(ASIO_NO_IOSTREAM) + +private: + // The underlying IP socket address. + union data_union + { + asio::detail::socket_addr_type base; + asio::detail::sockaddr_in4_type v4; + asio::detail::sockaddr_in6_type v6; + } data_; +}; + +} // namespace detail +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/detail/impl/endpoint.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_DETAIL_ENDPOINT_HPP diff --git a/subprojects/asio/include/asio/ip/detail/impl/endpoint.ipp b/subprojects/asio/include/asio/ip/detail/impl/endpoint.ipp new file mode 100644 index 000000000..304bdf3d3 --- /dev/null +++ b/subprojects/asio/include/asio/ip/detail/impl/endpoint.ipp @@ -0,0 +1,199 @@ +// +// ip/detail/impl/endpoint.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP +#define ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#if !defined(ASIO_NO_IOSTREAM) +# include +#endif // !defined(ASIO_NO_IOSTREAM) +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/ip/detail/endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { +namespace detail { + +endpoint::endpoint() + : data_() +{ + data_.v4.sin_family = ASIO_OS_DEF(AF_INET); + data_.v4.sin_port = 0; + data_.v4.sin_addr.s_addr = ASIO_OS_DEF(INADDR_ANY); +} + +endpoint::endpoint(int family, unsigned short port_num) + : data_() +{ + using namespace std; // For memcpy. + if (family == ASIO_OS_DEF(AF_INET)) + { + data_.v4.sin_family = ASIO_OS_DEF(AF_INET); + data_.v4.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v4.sin_addr.s_addr = ASIO_OS_DEF(INADDR_ANY); + } + else + { + data_.v6.sin6_family = ASIO_OS_DEF(AF_INET6); + data_.v6.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v6.sin6_flowinfo = 0; + data_.v6.sin6_addr.s6_addr[0] = 0; data_.v6.sin6_addr.s6_addr[1] = 0; + data_.v6.sin6_addr.s6_addr[2] = 0; data_.v6.sin6_addr.s6_addr[3] = 0; + data_.v6.sin6_addr.s6_addr[4] = 0; data_.v6.sin6_addr.s6_addr[5] = 0; + data_.v6.sin6_addr.s6_addr[6] = 0; data_.v6.sin6_addr.s6_addr[7] = 0; + data_.v6.sin6_addr.s6_addr[8] = 0; data_.v6.sin6_addr.s6_addr[9] = 0; + data_.v6.sin6_addr.s6_addr[10] = 0; data_.v6.sin6_addr.s6_addr[11] = 0; + data_.v6.sin6_addr.s6_addr[12] = 0; data_.v6.sin6_addr.s6_addr[13] = 0; + data_.v6.sin6_addr.s6_addr[14] = 0; data_.v6.sin6_addr.s6_addr[15] = 0; + data_.v6.sin6_scope_id = 0; + } +} + +endpoint::endpoint(const asio::ip::address& addr, + unsigned short port_num) + : data_() +{ + using namespace std; // For memcpy. + if (addr.is_v4()) + { + data_.v4.sin_family = ASIO_OS_DEF(AF_INET); + data_.v4.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v4.sin_addr.s_addr = + asio::detail::socket_ops::host_to_network_long( + addr.to_v4().to_uint()); + } + else + { + data_.v6.sin6_family = ASIO_OS_DEF(AF_INET6); + data_.v6.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v6.sin6_flowinfo = 0; + asio::ip::address_v6 v6_addr = addr.to_v6(); + asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes(); + memcpy(data_.v6.sin6_addr.s6_addr, bytes.data(), 16); + data_.v6.sin6_scope_id = + static_cast( + v6_addr.scope_id()); + } +} + +void endpoint::resize(std::size_t new_size) +{ + if (new_size > sizeof(asio::detail::sockaddr_storage_type)) + { + asio::error_code ec(asio::error::invalid_argument); + asio::detail::throw_error(ec); + } +} + +unsigned short endpoint::port() const +{ + if (is_v4()) + { + return asio::detail::socket_ops::network_to_host_short( + data_.v4.sin_port); + } + else + { + return asio::detail::socket_ops::network_to_host_short( + data_.v6.sin6_port); + } +} + +void endpoint::port(unsigned short port_num) +{ + if (is_v4()) + { + data_.v4.sin_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } + else + { + data_.v6.sin6_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } +} + +asio::ip::address endpoint::address() const +{ + using namespace std; // For memcpy. + if (is_v4()) + { + return asio::ip::address_v4( + asio::detail::socket_ops::network_to_host_long( + data_.v4.sin_addr.s_addr)); + } + else + { + asio::ip::address_v6::bytes_type bytes; +#if defined(ASIO_HAS_STD_ARRAY) + memcpy(bytes.data(), data_.v6.sin6_addr.s6_addr, 16); +#else // defined(ASIO_HAS_STD_ARRAY) + memcpy(bytes.elems, data_.v6.sin6_addr.s6_addr, 16); +#endif // defined(ASIO_HAS_STD_ARRAY) + return asio::ip::address_v6(bytes, data_.v6.sin6_scope_id); + } +} + +void endpoint::address(const asio::ip::address& addr) +{ + endpoint tmp_endpoint(addr, port()); + data_ = tmp_endpoint.data_; +} + +bool operator==(const endpoint& e1, const endpoint& e2) +{ + return e1.address() == e2.address() && e1.port() == e2.port(); +} + +bool operator<(const endpoint& e1, const endpoint& e2) +{ + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); +} + +#if !defined(ASIO_NO_IOSTREAM) +std::string endpoint::to_string() const +{ + std::ostringstream tmp_os; + tmp_os.imbue(std::locale::classic()); + if (is_v4()) + tmp_os << address(); + else + tmp_os << '[' << address() << ']'; + tmp_os << ':' << port(); + + return tmp_os.str(); +} +#endif // !defined(ASIO_NO_IOSTREAM) + +} // namespace detail +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP diff --git a/subprojects/asio/include/asio/ip/detail/socket_option.hpp b/subprojects/asio/include/asio/ip/detail/socket_option.hpp new file mode 100644 index 000000000..051ef30db --- /dev/null +++ b/subprojects/asio/include/asio/ip/detail/socket_option.hpp @@ -0,0 +1,566 @@ +// +// detail/socket_option.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_DETAIL_SOCKET_OPTION_HPP +#define ASIO_IP_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/ip/address.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { +namespace detail { +namespace socket_option { + +// Helper template for implementing multicast enable loopback options. +template +class multicast_enable_loopback +{ +public: +#if defined(__sun) || defined(__osf__) + typedef unsigned char ipv4_value_type; + typedef unsigned char ipv6_value_type; +#elif defined(_AIX) || defined(__hpux) || defined(__QNXNTO__) + typedef unsigned char ipv4_value_type; + typedef unsigned int ipv6_value_type; +#else + typedef int ipv4_value_type; + typedef int ipv6_value_type; +#endif + + // Default constructor. + multicast_enable_loopback() + : ipv4_value_(0), + ipv6_value_(0) + { + } + + // Construct with a specific option value. + explicit multicast_enable_loopback(bool v) + : ipv4_value_(v ? 1 : 0), + ipv6_value_(v ? 1 : 0) + { + } + + // Set the value of the boolean. + multicast_enable_loopback& operator=(bool v) + { + ipv4_value_ = v ? 1 : 0; + ipv6_value_ = v ? 1 : 0; + return *this; + } + + // Get the current value of the boolean. + bool value() const + { + return !!ipv4_value_; + } + + // Convert to bool. + operator bool() const + { + return !!ipv4_value_; + } + + // Test for false. + bool operator!() const + { + return !ipv4_value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the boolean data. + template + void* data(const Protocol& protocol) + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the address of the boolean data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the boolean data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + + // Set the size of the boolean data. + template + void resize(const Protocol& protocol, std::size_t s) + { + if (protocol.family() == PF_INET6) + { + if (s != sizeof(ipv6_value_)) + { + std::length_error ex("multicast_enable_loopback socket option resize"); + asio::detail::throw_exception(ex); + } + ipv4_value_ = ipv6_value_ ? 1 : 0; + } + else + { + if (s != sizeof(ipv4_value_)) + { + std::length_error ex("multicast_enable_loopback socket option resize"); + asio::detail::throw_exception(ex); + } + ipv6_value_ = ipv4_value_ ? 1 : 0; + } + } + +private: + ipv4_value_type ipv4_value_; + ipv6_value_type ipv6_value_; +}; + +// Helper template for implementing unicast hops options. +template +class unicast_hops +{ +public: + // Default constructor. + unicast_hops() + : value_(0) + { + } + + // Construct with a specific option value. + explicit unicast_hops(int v) + : value_(v) + { + } + + // Set the value of the option. + unicast_hops& operator=(int v) + { + value_ = v; + return *this; + } + + // Get the current value of the option. + int value() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + { + std::length_error ex("unicast hops socket option resize"); + asio::detail::throw_exception(ex); + } +#if defined(__hpux) + if (value_ < 0) + value_ = value_ & 0xFF; +#endif + } + +private: + int value_; +}; + +// Helper template for implementing multicast hops options. +template +class multicast_hops +{ +public: +#if defined(ASIO_WINDOWS) && defined(UNDER_CE) + typedef int ipv4_value_type; +#else + typedef unsigned char ipv4_value_type; +#endif + typedef int ipv6_value_type; + + // Default constructor. + multicast_hops() + : ipv4_value_(0), + ipv6_value_(0) + { + } + + // Construct with a specific option value. + explicit multicast_hops(int v) + { + if (v < 0 || v > 255) + { + std::out_of_range ex("multicast hops value out of range"); + asio::detail::throw_exception(ex); + } + ipv4_value_ = (ipv4_value_type)v; + ipv6_value_ = v; + } + + // Set the value of the option. + multicast_hops& operator=(int v) + { + if (v < 0 || v > 255) + { + std::out_of_range ex("multicast hops value out of range"); + asio::detail::throw_exception(ex); + } + ipv4_value_ = (ipv4_value_type)v; + ipv6_value_ = v; + return *this; + } + + // Get the current value of the option. + int value() const + { + return ipv6_value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the data. + template + void* data(const Protocol& protocol) + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the address of the data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + + // Set the size of the data. + template + void resize(const Protocol& protocol, std::size_t s) + { + if (protocol.family() == PF_INET6) + { + if (s != sizeof(ipv6_value_)) + { + std::length_error ex("multicast hops socket option resize"); + asio::detail::throw_exception(ex); + } + if (ipv6_value_ < 0) + ipv4_value_ = 0; + else if (ipv6_value_ > 255) + ipv4_value_ = 255; + else + ipv4_value_ = (ipv4_value_type)ipv6_value_; + } + else + { + if (s != sizeof(ipv4_value_)) + { + std::length_error ex("multicast hops socket option resize"); + asio::detail::throw_exception(ex); + } + ipv6_value_ = ipv4_value_; + } + } + +private: + ipv4_value_type ipv4_value_; + ipv6_value_type ipv6_value_; +}; + +// Helper template for implementing ip_mreq-based options. +template +class multicast_request +{ +public: + // Default constructor. + multicast_request() + : ipv4_value_(), // Zero-initialisation gives the "any" address. + ipv6_value_() // Zero-initialisation gives the "any" address. + { + } + + // Construct with multicast address only. + explicit multicast_request(const address& multicast_address) + : ipv4_value_(), // Zero-initialisation gives the "any" address. + ipv6_value_() // Zero-initialisation gives the "any" address. + { + if (multicast_address.is_v6()) + { + using namespace std; // For memcpy. + address_v6 ipv6_address = multicast_address.to_v6(); + address_v6::bytes_type bytes = ipv6_address.to_bytes(); + memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.data(), 16); + ipv6_value_.ipv6mr_interface = ipv6_address.scope_id(); + } + else + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + multicast_address.to_v4().to_uint()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + address_v4::any().to_uint()); + } + } + + // Construct with multicast address and IPv4 address specifying an interface. + explicit multicast_request(const address_v4& multicast_address, + const address_v4& network_interface = address_v4::any()) + : ipv6_value_() // Zero-initialisation gives the "any" address. + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + multicast_address.to_uint()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + network_interface.to_uint()); + } + + // Construct with multicast address and IPv6 network interface index. + explicit multicast_request( + const address_v6& multicast_address, + unsigned long network_interface = 0) + : ipv4_value_() // Zero-initialisation gives the "any" address. + { + using namespace std; // For memcpy. + address_v6::bytes_type bytes = multicast_address.to_bytes(); + memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.data(), 16); + if (network_interface) + ipv6_value_.ipv6mr_interface = network_interface; + else + ipv6_value_.ipv6mr_interface = multicast_address.scope_id(); + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the option data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the option data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + +private: + asio::detail::in4_mreq_type ipv4_value_; + asio::detail::in6_mreq_type ipv6_value_; +}; + +// Helper template for implementing options that specify a network interface. +template +class network_interface +{ +public: + // Default constructor. + network_interface() + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + address_v4::any().to_uint()); + ipv6_value_ = 0; + } + + // Construct with IPv4 interface. + explicit network_interface(const address_v4& ipv4_interface) + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + ipv4_interface.to_uint()); + ipv6_value_ = 0; + } + + // Construct with IPv6 interface. + explicit network_interface(unsigned int ipv6_interface) + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + address_v4::any().to_uint()); + ipv6_value_ = ipv6_interface; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the option data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the option data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + +private: + asio::detail::in4_addr_type ipv4_value_; + unsigned int ipv6_value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_DETAIL_SOCKET_OPTION_HPP diff --git a/subprojects/asio/include/asio/ip/host_name.hpp b/subprojects/asio/include/asio/ip/host_name.hpp new file mode 100644 index 000000000..d06de5089 --- /dev/null +++ b/subprojects/asio/include/asio/ip/host_name.hpp @@ -0,0 +1,42 @@ +// +// ip/host_name.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_HOST_NAME_HPP +#define ASIO_IP_HOST_NAME_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Get the current host name. +ASIO_DECL std::string host_name(); + +/// Get the current host name. +ASIO_DECL std::string host_name(asio::error_code& ec); + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/host_name.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_HOST_NAME_HPP diff --git a/subprojects/asio/include/asio/ip/icmp.hpp b/subprojects/asio/include/asio/ip/icmp.hpp new file mode 100644 index 000000000..92e1953f3 --- /dev/null +++ b/subprojects/asio/include/asio/ip/icmp.hpp @@ -0,0 +1,115 @@ +// +// ip/icmp.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ICMP_HPP +#define ASIO_IP_ICMP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/basic_raw_socket.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for ICMP. +/** + * The asio::ip::icmp class contains flags necessary for ICMP sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol, InternetProtocol. + */ +class icmp +{ +public: + /// The type of a ICMP endpoint. + typedef basic_endpoint endpoint; + + /// Construct to represent the IPv4 ICMP protocol. + static icmp v4() + { + return icmp(ASIO_OS_DEF(IPPROTO_ICMP), + ASIO_OS_DEF(AF_INET)); + } + + /// Construct to represent the IPv6 ICMP protocol. + static icmp v6() + { + return icmp(ASIO_OS_DEF(IPPROTO_ICMPV6), + ASIO_OS_DEF(AF_INET6)); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return ASIO_OS_DEF(SOCK_RAW); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return protocol_; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The ICMP socket type. + typedef basic_raw_socket socket; + + /// The ICMP resolver type. + typedef basic_resolver resolver; + + /// Compare two protocols for equality. + friend bool operator==(const icmp& p1, const icmp& p2) + { + return p1.protocol_ == p2.protocol_ && p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const icmp& p1, const icmp& p2) + { + return p1.protocol_ != p2.protocol_ || p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit icmp(int protocol_id, int protocol_family) + : protocol_(protocol_id), + family_(protocol_family) + { + } + + int protocol_; + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ICMP_HPP diff --git a/subprojects/asio/include/asio/ip/impl/address.hpp b/subprojects/asio/include/asio/ip/impl/address.hpp new file mode 100644 index 000000000..085b93f7b --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/address.hpp @@ -0,0 +1,67 @@ +// +// ip/impl/address.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_HPP +#define ASIO_IP_IMPL_ADDRESS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +#if !defined(ASIO_NO_DEPRECATED) + +inline address address::from_string(const char* str) +{ + return asio::ip::make_address(str); +} + +inline address address::from_string( + const char* str, asio::error_code& ec) +{ + return asio::ip::make_address(str, ec); +} + +inline address address::from_string(const std::string& str) +{ + return asio::ip::make_address(str); +} + +inline address address::from_string( + const std::string& str, asio::error_code& ec) +{ + return asio::ip::make_address(str, ec); +} + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address& addr) +{ + return os << addr.to_string().c_str(); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_ADDRESS_HPP diff --git a/subprojects/asio/include/asio/ip/impl/address.ipp b/subprojects/asio/include/asio/ip/impl/address.ipp new file mode 100644 index 000000000..0523071fe --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/address.ipp @@ -0,0 +1,234 @@ +// +// ip/impl/address.ipp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_IPP +#define ASIO_IP_IMPL_ADDRESS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/error.hpp" +#include "asio/ip/address.hpp" +#include "asio/ip/bad_address_cast.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +address::address() + : type_(ipv4), + ipv4_address_(), + ipv6_address_() +{ +} + +address::address(const asio::ip::address_v4& ipv4_address) + : type_(ipv4), + ipv4_address_(ipv4_address), + ipv6_address_() +{ +} + +address::address(const asio::ip::address_v6& ipv6_address) + : type_(ipv6), + ipv4_address_(), + ipv6_address_(ipv6_address) +{ +} + +address::address(const address& other) + : type_(other.type_), + ipv4_address_(other.ipv4_address_), + ipv6_address_(other.ipv6_address_) +{ +} + +#if defined(ASIO_HAS_MOVE) +address::address(address&& other) + : type_(other.type_), + ipv4_address_(other.ipv4_address_), + ipv6_address_(other.ipv6_address_) +{ +} +#endif // defined(ASIO_HAS_MOVE) + +address& address::operator=(const address& other) +{ + type_ = other.type_; + ipv4_address_ = other.ipv4_address_; + ipv6_address_ = other.ipv6_address_; + return *this; +} + +#if defined(ASIO_HAS_MOVE) +address& address::operator=(address&& other) +{ + type_ = other.type_; + ipv4_address_ = other.ipv4_address_; + ipv6_address_ = other.ipv6_address_; + return *this; +} +#endif // defined(ASIO_HAS_MOVE) + +address& address::operator=(const asio::ip::address_v4& ipv4_address) +{ + type_ = ipv4; + ipv4_address_ = ipv4_address; + ipv6_address_ = asio::ip::address_v6(); + return *this; +} + +address& address::operator=(const asio::ip::address_v6& ipv6_address) +{ + type_ = ipv6; + ipv4_address_ = asio::ip::address_v4(); + ipv6_address_ = ipv6_address; + return *this; +} + +address make_address(const char* str) +{ + asio::error_code ec; + address addr = make_address(str, ec); + asio::detail::throw_error(ec); + return addr; +} + +address make_address(const char* str, asio::error_code& ec) +{ + asio::ip::address_v6 ipv6_address = + asio::ip::make_address_v6(str, ec); + if (!ec) + return address(ipv6_address); + + asio::ip::address_v4 ipv4_address = + asio::ip::make_address_v4(str, ec); + if (!ec) + return address(ipv4_address); + + return address(); +} + +address make_address(const std::string& str) +{ + return make_address(str.c_str()); +} + +address make_address(const std::string& str, + asio::error_code& ec) +{ + return make_address(str.c_str(), ec); +} + +#if defined(ASIO_HAS_STRING_VIEW) + +address make_address(string_view str) +{ + return make_address(static_cast(str)); +} + +address make_address(string_view str, + asio::error_code& ec) +{ + return make_address(static_cast(str), ec); +} + +#endif // defined(ASIO_HAS_STRING_VIEW) + +asio::ip::address_v4 address::to_v4() const +{ + if (type_ != ipv4) + { + bad_address_cast ex; + asio::detail::throw_exception(ex); + } + return ipv4_address_; +} + +asio::ip::address_v6 address::to_v6() const +{ + if (type_ != ipv6) + { + bad_address_cast ex; + asio::detail::throw_exception(ex); + } + return ipv6_address_; +} + +std::string address::to_string() const +{ + if (type_ == ipv6) + return ipv6_address_.to_string(); + return ipv4_address_.to_string(); +} + +#if !defined(ASIO_NO_DEPRECATED) +std::string address::to_string(asio::error_code& ec) const +{ + if (type_ == ipv6) + return ipv6_address_.to_string(ec); + return ipv4_address_.to_string(ec); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +bool address::is_loopback() const +{ + return (type_ == ipv4) + ? ipv4_address_.is_loopback() + : ipv6_address_.is_loopback(); +} + +bool address::is_unspecified() const +{ + return (type_ == ipv4) + ? ipv4_address_.is_unspecified() + : ipv6_address_.is_unspecified(); +} + +bool address::is_multicast() const +{ + return (type_ == ipv4) + ? ipv4_address_.is_multicast() + : ipv6_address_.is_multicast(); +} + +bool operator==(const address& a1, const address& a2) +{ + if (a1.type_ != a2.type_) + return false; + if (a1.type_ == address::ipv6) + return a1.ipv6_address_ == a2.ipv6_address_; + return a1.ipv4_address_ == a2.ipv4_address_; +} + +bool operator<(const address& a1, const address& a2) +{ + if (a1.type_ < a2.type_) + return true; + if (a1.type_ > a2.type_) + return false; + if (a1.type_ == address::ipv6) + return a1.ipv6_address_ < a2.ipv6_address_; + return a1.ipv4_address_ < a2.ipv4_address_; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_ADDRESS_IPP diff --git a/subprojects/asio/include/asio/ip/impl/address_v4.hpp b/subprojects/asio/include/asio/ip/impl/address_v4.hpp new file mode 100644 index 000000000..d1cf407c3 --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/address_v4.hpp @@ -0,0 +1,67 @@ +// +// ip/impl/address_v4.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V4_HPP +#define ASIO_IP_IMPL_ADDRESS_V4_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +#if !defined(ASIO_NO_DEPRECATED) + +inline address_v4 address_v4::from_string(const char* str) +{ + return asio::ip::make_address_v4(str); +} + +inline address_v4 address_v4::from_string( + const char* str, asio::error_code& ec) +{ + return asio::ip::make_address_v4(str, ec); +} + +inline address_v4 address_v4::from_string(const std::string& str) +{ + return asio::ip::make_address_v4(str); +} + +inline address_v4 address_v4::from_string( + const std::string& str, asio::error_code& ec) +{ + return asio::ip::make_address_v4(str, ec); +} + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v4& addr) +{ + return os << addr.to_string().c_str(); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_ADDRESS_V4_HPP diff --git a/subprojects/asio/include/asio/ip/impl/address_v4.ipp b/subprojects/asio/include/asio/ip/impl/address_v4.ipp new file mode 100644 index 000000000..9559addb6 --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/address_v4.ipp @@ -0,0 +1,210 @@ +// +// ip/impl/address_v4.ipp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V4_IPP +#define ASIO_IP_IMPL_ADDRESS_V4_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include "asio/error.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/ip/address_v4.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +address_v4::address_v4(const address_v4::bytes_type& bytes) +{ +#if UCHAR_MAX > 0xFF + if (bytes[0] > 0xFF || bytes[1] > 0xFF + || bytes[2] > 0xFF || bytes[3] > 0xFF) + { + std::out_of_range ex("address_v4 from bytes_type"); + asio::detail::throw_exception(ex); + } +#endif // UCHAR_MAX > 0xFF + + using namespace std; // For memcpy. + memcpy(&addr_.s_addr, bytes.data(), 4); +} + +address_v4::address_v4(address_v4::uint_type addr) +{ + if ((std::numeric_limits::max)() > 0xFFFFFFFF) + { + std::out_of_range ex("address_v4 from unsigned integer"); + asio::detail::throw_exception(ex); + } + + addr_.s_addr = asio::detail::socket_ops::host_to_network_long( + static_cast(addr)); +} + +address_v4::bytes_type address_v4::to_bytes() const +{ + using namespace std; // For memcpy. + bytes_type bytes; +#if defined(ASIO_HAS_STD_ARRAY) + memcpy(bytes.data(), &addr_.s_addr, 4); +#else // defined(ASIO_HAS_STD_ARRAY) + memcpy(bytes.elems, &addr_.s_addr, 4); +#endif // defined(ASIO_HAS_STD_ARRAY) + return bytes; +} + +address_v4::uint_type address_v4::to_uint() const +{ + return asio::detail::socket_ops::network_to_host_long(addr_.s_addr); +} + +#if !defined(ASIO_NO_DEPRECATED) +unsigned long address_v4::to_ulong() const +{ + return asio::detail::socket_ops::network_to_host_long(addr_.s_addr); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +std::string address_v4::to_string() const +{ + asio::error_code ec; + char addr_str[asio::detail::max_addr_v4_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop( + ASIO_OS_DEF(AF_INET), &addr_, addr_str, + asio::detail::max_addr_v4_str_len, 0, ec); + if (addr == 0) + asio::detail::throw_error(ec); + return addr; +} + +#if !defined(ASIO_NO_DEPRECATED) +std::string address_v4::to_string(asio::error_code& ec) const +{ + char addr_str[asio::detail::max_addr_v4_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop( + ASIO_OS_DEF(AF_INET), &addr_, addr_str, + asio::detail::max_addr_v4_str_len, 0, ec); + if (addr == 0) + return std::string(); + return addr; +} +#endif // !defined(ASIO_NO_DEPRECATED) + +bool address_v4::is_loopback() const +{ + return (to_uint() & 0xFF000000) == 0x7F000000; +} + +bool address_v4::is_unspecified() const +{ + return to_uint() == 0; +} + +#if !defined(ASIO_NO_DEPRECATED) +bool address_v4::is_class_a() const +{ + return (to_uint() & 0x80000000) == 0; +} + +bool address_v4::is_class_b() const +{ + return (to_uint() & 0xC0000000) == 0x80000000; +} + +bool address_v4::is_class_c() const +{ + return (to_uint() & 0xE0000000) == 0xC0000000; +} +#endif // !defined(ASIO_NO_DEPRECATED) + +bool address_v4::is_multicast() const +{ + return (to_uint() & 0xF0000000) == 0xE0000000; +} + +#if !defined(ASIO_NO_DEPRECATED) +address_v4 address_v4::broadcast(const address_v4& addr, const address_v4& mask) +{ + return address_v4(addr.to_uint() | (mask.to_uint() ^ 0xFFFFFFFF)); +} + +address_v4 address_v4::netmask(const address_v4& addr) +{ + if (addr.is_class_a()) + return address_v4(0xFF000000); + if (addr.is_class_b()) + return address_v4(0xFFFF0000); + if (addr.is_class_c()) + return address_v4(0xFFFFFF00); + return address_v4(0xFFFFFFFF); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +address_v4 make_address_v4(const char* str) +{ + asio::error_code ec; + address_v4 addr = make_address_v4(str, ec); + asio::detail::throw_error(ec); + return addr; +} + +address_v4 make_address_v4( + const char* str, asio::error_code& ec) +{ + address_v4::bytes_type bytes; + if (asio::detail::socket_ops::inet_pton( + ASIO_OS_DEF(AF_INET), str, &bytes, 0, ec) <= 0) + return address_v4(); + return address_v4(bytes); +} + +address_v4 make_address_v4(const std::string& str) +{ + return make_address_v4(str.c_str()); +} + +address_v4 make_address_v4( + const std::string& str, asio::error_code& ec) +{ + return make_address_v4(str.c_str(), ec); +} + +#if defined(ASIO_HAS_STRING_VIEW) + +address_v4 make_address_v4(string_view str) +{ + return make_address_v4(static_cast(str)); +} + +address_v4 make_address_v4(string_view str, + asio::error_code& ec) +{ + return make_address_v4(static_cast(str), ec); +} + +#endif // defined(ASIO_HAS_STRING_VIEW) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_ADDRESS_V4_IPP diff --git a/subprojects/asio/include/asio/ip/impl/address_v6.hpp b/subprojects/asio/include/asio/ip/impl/address_v6.hpp new file mode 100644 index 000000000..330eafd1f --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/address_v6.hpp @@ -0,0 +1,67 @@ +// +// ip/impl/address_v6.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V6_HPP +#define ASIO_IP_IMPL_ADDRESS_V6_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +#if !defined(ASIO_NO_DEPRECATED) + +inline address_v6 address_v6::from_string(const char* str) +{ + return asio::ip::make_address_v6(str); +} + +inline address_v6 address_v6::from_string( + const char* str, asio::error_code& ec) +{ + return asio::ip::make_address_v6(str, ec); +} + +inline address_v6 address_v6::from_string(const std::string& str) +{ + return asio::ip::make_address_v6(str); +} + +inline address_v6 address_v6::from_string( + const std::string& str, asio::error_code& ec) +{ + return asio::ip::make_address_v6(str, ec); +} + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v6& addr) +{ + return os << addr.to_string().c_str(); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_ADDRESS_V6_HPP diff --git a/subprojects/asio/include/asio/ip/impl/address_v6.ipp b/subprojects/asio/include/asio/ip/impl/address_v6.ipp new file mode 100644 index 000000000..36bd68cff --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/address_v6.ipp @@ -0,0 +1,350 @@ +// +// ip/impl/address_v6.ipp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V6_IPP +#define ASIO_IP_IMPL_ADDRESS_V6_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/error.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/ip/bad_address_cast.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +address_v6::address_v6() + : addr_(), + scope_id_(0) +{ +} + +address_v6::address_v6(const address_v6::bytes_type& bytes, + unsigned long scope) + : scope_id_(scope) +{ +#if UCHAR_MAX > 0xFF + for (std::size_t i = 0; i < bytes.size(); ++i) + { + if (bytes[i] > 0xFF) + { + std::out_of_range ex("address_v6 from bytes_type"); + asio::detail::throw_exception(ex); + } + } +#endif // UCHAR_MAX > 0xFF + + using namespace std; // For memcpy. + memcpy(addr_.s6_addr, bytes.data(), 16); +} + +address_v6::address_v6(const address_v6& other) + : addr_(other.addr_), + scope_id_(other.scope_id_) +{ +} + +#if defined(ASIO_HAS_MOVE) +address_v6::address_v6(address_v6&& other) + : addr_(other.addr_), + scope_id_(other.scope_id_) +{ +} +#endif // defined(ASIO_HAS_MOVE) + +address_v6& address_v6::operator=(const address_v6& other) +{ + addr_ = other.addr_; + scope_id_ = other.scope_id_; + return *this; +} + +#if defined(ASIO_HAS_MOVE) +address_v6& address_v6::operator=(address_v6&& other) +{ + addr_ = other.addr_; + scope_id_ = other.scope_id_; + return *this; +} +#endif // defined(ASIO_HAS_MOVE) + +address_v6::bytes_type address_v6::to_bytes() const +{ + using namespace std; // For memcpy. + bytes_type bytes; +#if defined(ASIO_HAS_STD_ARRAY) + memcpy(bytes.data(), addr_.s6_addr, 16); +#else // defined(ASIO_HAS_STD_ARRAY) + memcpy(bytes.elems, addr_.s6_addr, 16); +#endif // defined(ASIO_HAS_STD_ARRAY) + return bytes; +} + +std::string address_v6::to_string() const +{ + asio::error_code ec; + char addr_str[asio::detail::max_addr_v6_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop( + ASIO_OS_DEF(AF_INET6), &addr_, addr_str, + asio::detail::max_addr_v6_str_len, scope_id_, ec); + if (addr == 0) + asio::detail::throw_error(ec); + return addr; +} + +#if !defined(ASIO_NO_DEPRECATED) +std::string address_v6::to_string(asio::error_code& ec) const +{ + char addr_str[asio::detail::max_addr_v6_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop( + ASIO_OS_DEF(AF_INET6), &addr_, addr_str, + asio::detail::max_addr_v6_str_len, scope_id_, ec); + if (addr == 0) + return std::string(); + return addr; +} + +address_v4 address_v6::to_v4() const +{ + if (!is_v4_mapped() && !is_v4_compatible()) + { + bad_address_cast ex; + asio::detail::throw_exception(ex); + } + + address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12], + addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } }; + return address_v4(v4_bytes); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +bool address_v6::is_loopback() const +{ + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1)); +} + +bool address_v6::is_unspecified() const +{ + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0)); +} + +bool address_v6::is_link_local() const +{ + return ((addr_.s6_addr[0] == 0xfe) && ((addr_.s6_addr[1] & 0xc0) == 0x80)); +} + +bool address_v6::is_site_local() const +{ + return ((addr_.s6_addr[0] == 0xfe) && ((addr_.s6_addr[1] & 0xc0) == 0xc0)); +} + +bool address_v6::is_v4_mapped() const +{ + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0xff) && (addr_.s6_addr[11] == 0xff)); +} + +#if !defined(ASIO_NO_DEPRECATED) +bool address_v6::is_v4_compatible() const +{ + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && !((addr_.s6_addr[12] == 0) + && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) + && ((addr_.s6_addr[15] == 0) || (addr_.s6_addr[15] == 1)))); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +bool address_v6::is_multicast() const +{ + return (addr_.s6_addr[0] == 0xff); +} + +bool address_v6::is_multicast_global() const +{ + return ((addr_.s6_addr[0] == 0xff) && ((addr_.s6_addr[1] & 0x0f) == 0x0e)); +} + +bool address_v6::is_multicast_link_local() const +{ + return ((addr_.s6_addr[0] == 0xff) && ((addr_.s6_addr[1] & 0x0f) == 0x02)); +} + +bool address_v6::is_multicast_node_local() const +{ + return ((addr_.s6_addr[0] == 0xff) && ((addr_.s6_addr[1] & 0x0f) == 0x01)); +} + +bool address_v6::is_multicast_org_local() const +{ + return ((addr_.s6_addr[0] == 0xff) && ((addr_.s6_addr[1] & 0x0f) == 0x08)); +} + +bool address_v6::is_multicast_site_local() const +{ + return ((addr_.s6_addr[0] == 0xff) && ((addr_.s6_addr[1] & 0x0f) == 0x05)); +} + +bool operator==(const address_v6& a1, const address_v6& a2) +{ + using namespace std; // For memcmp. + return memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) == 0 + && a1.scope_id_ == a2.scope_id_; +} + +bool operator<(const address_v6& a1, const address_v6& a2) +{ + using namespace std; // For memcmp. + int memcmp_result = memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)); + if (memcmp_result < 0) + return true; + if (memcmp_result > 0) + return false; + return a1.scope_id_ < a2.scope_id_; +} + +address_v6 address_v6::loopback() +{ + address_v6 tmp; + tmp.addr_.s6_addr[15] = 1; + return tmp; +} + +#if !defined(ASIO_NO_DEPRECATED) +address_v6 address_v6::v4_mapped(const address_v4& addr) +{ + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; + return address_v6(v6_bytes); +} + +address_v6 address_v6::v4_compatible(const address_v4& addr) +{ + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; + return address_v6(v6_bytes); +} +#endif // !defined(ASIO_NO_DEPRECATED) + +address_v6 make_address_v6(const char* str) +{ + asio::error_code ec; + address_v6 addr = make_address_v6(str, ec); + asio::detail::throw_error(ec); + return addr; +} + +address_v6 make_address_v6( + const char* str, asio::error_code& ec) +{ + address_v6::bytes_type bytes; + unsigned long scope_id = 0; + if (asio::detail::socket_ops::inet_pton( + ASIO_OS_DEF(AF_INET6), str, &bytes[0], &scope_id, ec) <= 0) + return address_v6(); + return address_v6(bytes, scope_id); +} + +address_v6 make_address_v6(const std::string& str) +{ + return make_address_v6(str.c_str()); +} + +address_v6 make_address_v6( + const std::string& str, asio::error_code& ec) +{ + return make_address_v6(str.c_str(), ec); +} + +#if defined(ASIO_HAS_STRING_VIEW) + +address_v6 make_address_v6(string_view str) +{ + return make_address_v6(static_cast(str)); +} + +address_v6 make_address_v6(string_view str, + asio::error_code& ec) +{ + return make_address_v6(static_cast(str), ec); +} + +#endif // defined(ASIO_HAS_STRING_VIEW) + +address_v4 make_address_v4( + v4_mapped_t, const address_v6& v6_addr) +{ + if (!v6_addr.is_v4_mapped()) + { + bad_address_cast ex; + asio::detail::throw_exception(ex); + } + + address_v6::bytes_type v6_bytes = v6_addr.to_bytes(); + address_v4::bytes_type v4_bytes = { { v6_bytes[12], + v6_bytes[13], v6_bytes[14], v6_bytes[15] } }; + return address_v4(v4_bytes); +} + +address_v6 make_address_v6( + v4_mapped_t, const address_v4& v4_addr) +{ + address_v4::bytes_type v4_bytes = v4_addr.to_bytes(); + address_v6::bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF, 0xFF, v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; + return address_v6(v6_bytes); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_ADDRESS_V6_IPP diff --git a/subprojects/asio/include/asio/ip/impl/basic_endpoint.hpp b/subprojects/asio/include/asio/ip/impl/basic_endpoint.hpp new file mode 100644 index 000000000..5680a4381 --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/basic_endpoint.hpp @@ -0,0 +1,43 @@ +// +// ip/impl/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_BASIC_ENDPOINT_HPP +#define ASIO_IP_IMPL_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint) +{ + asio::ip::detail::endpoint tmp_ep(endpoint.address(), endpoint.port()); + return os << tmp_ep.to_string().c_str(); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_BASIC_ENDPOINT_HPP diff --git a/subprojects/asio/include/asio/ip/impl/host_name.ipp b/subprojects/asio/include/asio/ip/impl/host_name.ipp new file mode 100644 index 000000000..932ab1c6d --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/host_name.ipp @@ -0,0 +1,54 @@ +// +// ip/impl/host_name.ipp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_HOST_NAME_IPP +#define ASIO_IP_IMPL_HOST_NAME_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/ip/host_name.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +std::string host_name() +{ + char name[1024]; + asio::error_code ec; + if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) + { + asio::detail::throw_error(ec); + return std::string(); + } + return std::string(name); +} + +std::string host_name(asio::error_code& ec) +{ + char name[1024]; + if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) + return std::string(); + return std::string(name); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_HOST_NAME_IPP diff --git a/subprojects/asio/include/asio/ip/impl/network_v4.hpp b/subprojects/asio/include/asio/ip/impl/network_v4.hpp new file mode 100644 index 000000000..911a45c3f --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/network_v4.hpp @@ -0,0 +1,54 @@ +// +// ip/impl/network_v4.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2014 Oliver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_NETWORK_V4_HPP +#define ASIO_IP_IMPL_NETWORK_V4_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const network_v4& addr) +{ + asio::error_code ec; + std::string s = addr.to_string(ec); + if (ec) + { + if (os.exceptions() & std::basic_ostream::failbit) + asio::detail::throw_error(ec); + else + os.setstate(std::basic_ostream::failbit); + } + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_NETWORK_V4_HPP diff --git a/subprojects/asio/include/asio/ip/impl/network_v4.ipp b/subprojects/asio/include/asio/ip/impl/network_v4.ipp new file mode 100644 index 000000000..4b278871b --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/network_v4.ipp @@ -0,0 +1,216 @@ +// +// ip/impl/network_v4.ipp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2014 Oliver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_NETWORK_V4_IPP +#define ASIO_IP_IMPL_NETWORK_V4_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include "asio/error.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/ip/network_v4.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +network_v4::network_v4(const address_v4& addr, unsigned short prefix_len) + : address_(addr), + prefix_length_(prefix_len) +{ + if (prefix_len > 32) + { + std::out_of_range ex("prefix length too large"); + asio::detail::throw_exception(ex); + } +} + +network_v4::network_v4(const address_v4& addr, const address_v4& mask) + : address_(addr), + prefix_length_(0) +{ + address_v4::bytes_type mask_bytes = mask.to_bytes(); + bool finished = false; + for (std::size_t i = 0; i < mask_bytes.size(); ++i) + { + if (finished) + { + if (mask_bytes[i]) + { + std::invalid_argument ex("non-contiguous netmask"); + asio::detail::throw_exception(ex); + } + continue; + } + else + { + switch (mask_bytes[i]) + { + case 255: + prefix_length_ += 8; + break; + case 254: // prefix_length_ += 7 + prefix_length_ += 1; + case 252: // prefix_length_ += 6 + prefix_length_ += 1; + case 248: // prefix_length_ += 5 + prefix_length_ += 1; + case 240: // prefix_length_ += 4 + prefix_length_ += 1; + case 224: // prefix_length_ += 3 + prefix_length_ += 1; + case 192: // prefix_length_ += 2 + prefix_length_ += 1; + case 128: // prefix_length_ += 1 + prefix_length_ += 1; + case 0: // nbits += 0 + finished = true; + break; + default: + std::out_of_range ex("non-contiguous netmask"); + asio::detail::throw_exception(ex); + } + } + } +} + +address_v4 network_v4::netmask() const ASIO_NOEXCEPT +{ + uint32_t nmbits = 0xffffffff; + if (prefix_length_ == 0) + nmbits = 0; + else + nmbits = nmbits << (32 - prefix_length_); + return address_v4(nmbits); +} + +address_v4_range network_v4::hosts() const ASIO_NOEXCEPT +{ + return is_host() + ? address_v4_range(address_, address_v4(address_.to_uint() + 1)) + : address_v4_range(address_v4(network().to_uint() + 1), broadcast()); +} + +bool network_v4::is_subnet_of(const network_v4& other) const +{ + if (other.prefix_length_ >= prefix_length_) + return false; // Only real subsets are allowed. + const network_v4 me(address_, other.prefix_length_); + return other.canonical() == me.canonical(); +} + +std::string network_v4::to_string() const +{ + asio::error_code ec; + std::string addr = to_string(ec); + asio::detail::throw_error(ec); + return addr; +} + +std::string network_v4::to_string(asio::error_code& ec) const +{ + using namespace std; // For sprintf. + ec = asio::error_code(); + char prefix_len[16]; +#if defined(ASIO_HAS_SECURE_RTL) + sprintf_s(prefix_len, sizeof(prefix_len), "/%u", prefix_length_); +#else // defined(ASIO_HAS_SECURE_RTL) + sprintf(prefix_len, "/%u", prefix_length_); +#endif // defined(ASIO_HAS_SECURE_RTL) + return address_.to_string() + prefix_len; +} + +network_v4 make_network_v4(const char* str) +{ + return make_network_v4(std::string(str)); +} + +network_v4 make_network_v4(const char* str, asio::error_code& ec) +{ + return make_network_v4(std::string(str), ec); +} + +network_v4 make_network_v4(const std::string& str) +{ + asio::error_code ec; + network_v4 net = make_network_v4(str, ec); + asio::detail::throw_error(ec); + return net; +} + +network_v4 make_network_v4(const std::string& str, + asio::error_code& ec) +{ + std::string::size_type pos = str.find_first_of("/"); + + if (pos == std::string::npos) + { + ec = asio::error::invalid_argument; + return network_v4(); + } + + if (pos == str.size() - 1) + { + ec = asio::error::invalid_argument; + return network_v4(); + } + + std::string::size_type end = str.find_first_not_of("0123456789", pos + 1); + if (end != std::string::npos) + { + ec = asio::error::invalid_argument; + return network_v4(); + } + + const address_v4 addr = make_address_v4(str.substr(0, pos), ec); + if (ec) + return network_v4(); + + const int prefix_len = std::atoi(str.substr(pos + 1).c_str()); + if (prefix_len < 0 || prefix_len > 32) + { + ec = asio::error::invalid_argument; + return network_v4(); + } + + return network_v4(addr, static_cast(prefix_len)); +} + +#if defined(ASIO_HAS_STRING_VIEW) + +network_v4 make_network_v4(string_view str) +{ + return make_network_v4(static_cast(str)); +} + +network_v4 make_network_v4(string_view str, + asio::error_code& ec) +{ + return make_network_v4(static_cast(str), ec); +} + +#endif // defined(ASIO_HAS_STRING_VIEW) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_NETWORK_V4_IPP diff --git a/subprojects/asio/include/asio/ip/impl/network_v6.hpp b/subprojects/asio/include/asio/ip/impl/network_v6.hpp new file mode 100644 index 000000000..b0eedaded --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/network_v6.hpp @@ -0,0 +1,53 @@ +// +// ip/impl/network_v6.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_NETWORK_V6_HPP +#define ASIO_IP_IMPL_NETWORK_V6_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const network_v6& addr) +{ + asio::error_code ec; + std::string s = addr.to_string(ec); + if (ec) + { + if (os.exceptions() & std::basic_ostream::failbit) + asio::detail::throw_error(ec); + else + os.setstate(std::basic_ostream::failbit); + } + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_NETWORK_V6_HPP diff --git a/subprojects/asio/include/asio/ip/impl/network_v6.ipp b/subprojects/asio/include/asio/ip/impl/network_v6.ipp new file mode 100644 index 000000000..2e37a99ad --- /dev/null +++ b/subprojects/asio/include/asio/ip/impl/network_v6.ipp @@ -0,0 +1,185 @@ +// +// ip/impl/network_v6.ipp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2014 Oliver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_NETWORK_V6_IPP +#define ASIO_IP_IMPL_NETWORK_V6_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include "asio/error.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/ip/network_v6.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +network_v6::network_v6(const address_v6& addr, unsigned short prefix_len) + : address_(addr), + prefix_length_(prefix_len) +{ + if (prefix_len > 128) + { + std::out_of_range ex("prefix length too large"); + asio::detail::throw_exception(ex); + } +} + +ASIO_DECL address_v6 network_v6::network() const ASIO_NOEXCEPT +{ + address_v6::bytes_type bytes(address_.to_bytes()); + for (std::size_t i = 0; i < 16; ++i) + { + if (prefix_length_ <= i * 8) + bytes[i] = 0; + else if (prefix_length_ < (i + 1) * 8) + bytes[i] &= 0xFF00 >> (prefix_length_ % 8); + } + return address_v6(bytes, address_.scope_id()); +} + +address_v6_range network_v6::hosts() const ASIO_NOEXCEPT +{ + address_v6::bytes_type begin_bytes(address_.to_bytes()); + address_v6::bytes_type end_bytes(address_.to_bytes()); + for (std::size_t i = 0; i < 16; ++i) + { + if (prefix_length_ <= i * 8) + { + begin_bytes[i] = 0; + end_bytes[i] = 0xFF; + } + else if (prefix_length_ < (i + 1) * 8) + { + begin_bytes[i] &= 0xFF00 >> (prefix_length_ % 8); + end_bytes[i] |= 0xFF >> (prefix_length_ % 8); + } + } + return address_v6_range( + address_v6_iterator(address_v6(begin_bytes, address_.scope_id())), + ++address_v6_iterator(address_v6(end_bytes, address_.scope_id()))); +} + +bool network_v6::is_subnet_of(const network_v6& other) const +{ + if (other.prefix_length_ >= prefix_length_) + return false; // Only real subsets are allowed. + const network_v6 me(address_, other.prefix_length_); + return other.canonical() == me.canonical(); +} + +std::string network_v6::to_string() const +{ + asio::error_code ec; + std::string addr = to_string(ec); + asio::detail::throw_error(ec); + return addr; +} + +std::string network_v6::to_string(asio::error_code& ec) const +{ + using namespace std; // For sprintf. + ec = asio::error_code(); + char prefix_len[16]; +#if defined(ASIO_HAS_SECURE_RTL) + sprintf_s(prefix_len, sizeof(prefix_len), "/%u", prefix_length_); +#else // defined(ASIO_HAS_SECURE_RTL) + sprintf(prefix_len, "/%u", prefix_length_); +#endif // defined(ASIO_HAS_SECURE_RTL) + return address_.to_string() + prefix_len; +} + +network_v6 make_network_v6(const char* str) +{ + return make_network_v6(std::string(str)); +} + +network_v6 make_network_v6(const char* str, asio::error_code& ec) +{ + return make_network_v6(std::string(str), ec); +} + +network_v6 make_network_v6(const std::string& str) +{ + asio::error_code ec; + network_v6 net = make_network_v6(str, ec); + asio::detail::throw_error(ec); + return net; +} + +network_v6 make_network_v6(const std::string& str, + asio::error_code& ec) +{ + std::string::size_type pos = str.find_first_of("/"); + + if (pos == std::string::npos) + { + ec = asio::error::invalid_argument; + return network_v6(); + } + + if (pos == str.size() - 1) + { + ec = asio::error::invalid_argument; + return network_v6(); + } + + std::string::size_type end = str.find_first_not_of("0123456789", pos + 1); + if (end != std::string::npos) + { + ec = asio::error::invalid_argument; + return network_v6(); + } + + const address_v6 addr = make_address_v6(str.substr(0, pos), ec); + if (ec) + return network_v6(); + + const int prefix_len = std::atoi(str.substr(pos + 1).c_str()); + if (prefix_len < 0 || prefix_len > 128) + { + ec = asio::error::invalid_argument; + return network_v6(); + } + + return network_v6(addr, static_cast(prefix_len)); +} + +#if defined(ASIO_HAS_STRING_VIEW) + +network_v6 make_network_v6(string_view str) +{ + return make_network_v6(static_cast(str)); +} + +network_v6 make_network_v6(string_view str, + asio::error_code& ec) +{ + return make_network_v6(static_cast(str), ec); +} + +#endif // defined(ASIO_HAS_STRING_VIEW) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_NETWORK_V6_IPP diff --git a/subprojects/asio/include/asio/ip/multicast.hpp b/subprojects/asio/include/asio/ip/multicast.hpp new file mode 100644 index 000000000..ea624e147 --- /dev/null +++ b/subprojects/asio/include/asio/ip/multicast.hpp @@ -0,0 +1,191 @@ +// +// ip/multicast.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_MULTICAST_HPP +#define ASIO_IP_MULTICAST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/ip/detail/socket_option.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { +namespace multicast { + +/// Socket option to join a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_ADD_MEMBERSHIP socket option. + * + * @par Examples + * Setting the option to join a multicast group: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::address multicast_address = + * asio::ip::address::from_string("225.0.0.1"); + * asio::ip::multicast::join_group option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined join_group; +#else +typedef asio::ip::detail::socket_option::multicast_request< + ASIO_OS_DEF(IPPROTO_IP), + ASIO_OS_DEF(IP_ADD_MEMBERSHIP), + ASIO_OS_DEF(IPPROTO_IPV6), + ASIO_OS_DEF(IPV6_JOIN_GROUP)> join_group; +#endif + +/// Socket option to leave a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_DROP_MEMBERSHIP socket option. + * + * @par Examples + * Setting the option to leave a multicast group: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::address multicast_address = + * asio::ip::address::from_string("225.0.0.1"); + * asio::ip::multicast::leave_group option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined leave_group; +#else +typedef asio::ip::detail::socket_option::multicast_request< + ASIO_OS_DEF(IPPROTO_IP), + ASIO_OS_DEF(IP_DROP_MEMBERSHIP), + ASIO_OS_DEF(IPPROTO_IPV6), + ASIO_OS_DEF(IPV6_LEAVE_GROUP)> leave_group; +#endif + +/// Socket option for local interface to use for outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_IF socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::address_v4 local_interface = + * asio::ip::address_v4::from_string("1.2.3.4"); + * asio::ip::multicast::outbound_interface option(local_interface); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined outbound_interface; +#else +typedef asio::ip::detail::socket_option::network_interface< + ASIO_OS_DEF(IPPROTO_IP), + ASIO_OS_DEF(IP_MULTICAST_IF), + ASIO_OS_DEF(IPPROTO_IPV6), + ASIO_OS_DEF(IPV6_MULTICAST_IF)> outbound_interface; +#endif + +/// Socket option for time-to-live associated with outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_TTL socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::multicast::hops option(4); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::multicast::hops option; + * socket.get_option(option); + * int ttl = option.value(); + * @endcode + * + * @par Concepts: + * GettableSocketOption, SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined hops; +#else +typedef asio::ip::detail::socket_option::multicast_hops< + ASIO_OS_DEF(IPPROTO_IP), + ASIO_OS_DEF(IP_MULTICAST_TTL), + ASIO_OS_DEF(IPPROTO_IPV6), + ASIO_OS_DEF(IPV6_MULTICAST_HOPS)> hops; +#endif + +/// Socket option determining whether outgoing multicast packets will be +/// received on the same socket if it is a member of the multicast group. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_LOOP socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::multicast::enable_loopback option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::multicast::enable_loopback option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * GettableSocketOption, SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined enable_loopback; +#else +typedef asio::ip::detail::socket_option::multicast_enable_loopback< + ASIO_OS_DEF(IPPROTO_IP), + ASIO_OS_DEF(IP_MULTICAST_LOOP), + ASIO_OS_DEF(IPPROTO_IPV6), + ASIO_OS_DEF(IPV6_MULTICAST_LOOP)> enable_loopback; +#endif + +} // namespace multicast +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_MULTICAST_HPP diff --git a/subprojects/asio/include/asio/ip/network_v4.hpp b/subprojects/asio/include/asio/ip/network_v4.hpp new file mode 100644 index 000000000..e38f60fe9 --- /dev/null +++ b/subprojects/asio/include/asio/ip/network_v4.hpp @@ -0,0 +1,261 @@ +// +// ip/network_v4.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2014 Oliver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_NETWORK_V4_HPP +#define ASIO_IP_NETWORK_V4_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/string_view.hpp" +#include "asio/error_code.hpp" +#include "asio/ip/address_v4_range.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Represents an IPv4 network. +/** + * The asio::ip::network_v4 class provides the ability to use and + * manipulate IP version 4 networks. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class network_v4 +{ +public: + /// Default constructor. + network_v4() ASIO_NOEXCEPT + : address_(), + prefix_length_(0) + { + } + + /// Construct a network based on the specified address and prefix length. + ASIO_DECL network_v4(const address_v4& addr, + unsigned short prefix_len); + + /// Construct network based on the specified address and netmask. + ASIO_DECL network_v4(const address_v4& addr, + const address_v4& mask); + + /// Copy constructor. + network_v4(const network_v4& other) ASIO_NOEXCEPT + : address_(other.address_), + prefix_length_(other.prefix_length_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + network_v4(network_v4&& other) ASIO_NOEXCEPT + : address_(ASIO_MOVE_CAST(address_v4)(other.address_)), + prefix_length_(other.prefix_length_) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from another network. + network_v4& operator=(const network_v4& other) ASIO_NOEXCEPT + { + address_ = other.address_; + prefix_length_ = other.prefix_length_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move-assign from another network. + network_v4& operator=(network_v4&& other) ASIO_NOEXCEPT + { + address_ = ASIO_MOVE_CAST(address_v4)(other.address_); + prefix_length_ = other.prefix_length_; + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// Obtain the address object specified when the network object was created. + address_v4 address() const ASIO_NOEXCEPT + { + return address_; + } + + /// Obtain the prefix length that was specified when the network object was + /// created. + unsigned short prefix_length() const ASIO_NOEXCEPT + { + return prefix_length_; + } + + /// Obtain the netmask that was specified when the network object was created. + ASIO_DECL address_v4 netmask() const ASIO_NOEXCEPT; + + /// Obtain an address object that represents the network address. + address_v4 network() const ASIO_NOEXCEPT + { + return address_v4(address_.to_uint() & netmask().to_uint()); + } + + /// Obtain an address object that represents the network's broadcast address. + address_v4 broadcast() const ASIO_NOEXCEPT + { + return address_v4(network().to_uint() | (netmask().to_uint() ^ 0xFFFFFFFF)); + } + + /// Obtain an address range corresponding to the hosts in the network. + ASIO_DECL address_v4_range hosts() const ASIO_NOEXCEPT; + + /// Obtain the true network address, omitting any host bits. + network_v4 canonical() const ASIO_NOEXCEPT + { + return network_v4(network(), netmask()); + } + + /// Test if network is a valid host address. + bool is_host() const ASIO_NOEXCEPT + { + return prefix_length_ == 32; + } + + /// Test if a network is a real subnet of another network. + ASIO_DECL bool is_subnet_of(const network_v4& other) const; + + /// Get the network as an address in dotted decimal format. + ASIO_DECL std::string to_string() const; + + /// Get the network as an address in dotted decimal format. + ASIO_DECL std::string to_string(asio::error_code& ec) const; + + /// Compare two networks for equality. + friend bool operator==(const network_v4& a, const network_v4& b) + { + return a.address_ == b.address_ && a.prefix_length_ == b.prefix_length_; + } + + /// Compare two networks for inequality. + friend bool operator!=(const network_v4& a, const network_v4& b) + { + return !(a == b); + } + +private: + address_v4 address_; + unsigned short prefix_length_; +}; + +/// Create an IPv4 network from an address and prefix length. +/** + * @relates address_v4 + */ +inline network_v4 make_network_v4( + const address_v4& addr, unsigned short prefix_len) +{ + return network_v4(addr, prefix_len); +} + +/// Create an IPv4 network from an address and netmask. +/** + * @relates address_v4 + */ +inline network_v4 make_network_v4( + const address_v4& addr, const address_v4& mask) +{ + return network_v4(addr, mask); +} + +/// Create an IPv4 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v4 + */ +ASIO_DECL network_v4 make_network_v4(const char* str); + +/// Create an IPv4 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v4 + */ +ASIO_DECL network_v4 make_network_v4( + const char* str, asio::error_code& ec); + +/// Create an IPv4 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v4 + */ +ASIO_DECL network_v4 make_network_v4(const std::string& str); + +/// Create an IPv4 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v4 + */ +ASIO_DECL network_v4 make_network_v4( + const std::string& str, asio::error_code& ec); + +#if defined(ASIO_HAS_STRING_VIEW) \ + || defined(GENERATING_DOCUMENTATION) + +/// Create an IPv4 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v4 + */ +ASIO_DECL network_v4 make_network_v4(string_view str); + +/// Create an IPv4 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v4 + */ +ASIO_DECL network_v4 make_network_v4( + string_view str, asio::error_code& ec); + +#endif // defined(ASIO_HAS_STRING_VIEW) + // || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_NO_IOSTREAM) + +/// Output a network as a string. +/** + * Used to output a human-readable string for a specified network. + * + * @param os The output stream to which the string will be written. + * + * @param net The network to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v4 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const network_v4& net); + +#endif // !defined(ASIO_NO_IOSTREAM) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/impl/network_v4.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/network_v4.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_NETWORK_V4_HPP diff --git a/subprojects/asio/include/asio/ip/network_v6.hpp b/subprojects/asio/include/asio/ip/network_v6.hpp new file mode 100644 index 000000000..7b6b7e613 --- /dev/null +++ b/subprojects/asio/include/asio/ip/network_v6.hpp @@ -0,0 +1,235 @@ +// +// ip/network_v6.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2014 Oliver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_NETWORK_V6_HPP +#define ASIO_IP_NETWORK_V6_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/string_view.hpp" +#include "asio/error_code.hpp" +#include "asio/ip/address_v6_range.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Represents an IPv6 network. +/** + * The asio::ip::network_v6 class provides the ability to use and + * manipulate IP version 6 networks. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class network_v6 +{ +public: + /// Default constructor. + network_v6() ASIO_NOEXCEPT + : address_(), + prefix_length_(0) + { + } + + /// Construct a network based on the specified address and prefix length. + ASIO_DECL network_v6(const address_v6& addr, + unsigned short prefix_len); + + /// Copy constructor. + network_v6(const network_v6& other) ASIO_NOEXCEPT + : address_(other.address_), + prefix_length_(other.prefix_length_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + network_v6(network_v6&& other) ASIO_NOEXCEPT + : address_(ASIO_MOVE_CAST(address_v6)(other.address_)), + prefix_length_(other.prefix_length_) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from another network. + network_v6& operator=(const network_v6& other) ASIO_NOEXCEPT + { + address_ = other.address_; + prefix_length_ = other.prefix_length_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move-assign from another network. + network_v6& operator=(network_v6&& other) ASIO_NOEXCEPT + { + address_ = ASIO_MOVE_CAST(address_v6)(other.address_); + prefix_length_ = other.prefix_length_; + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// Obtain the address object specified when the network object was created. + address_v6 address() const ASIO_NOEXCEPT + { + return address_; + } + + /// Obtain the prefix length that was specified when the network object was + /// created. + unsigned short prefix_length() const ASIO_NOEXCEPT + { + return prefix_length_; + } + + /// Obtain an address object that represents the network address. + ASIO_DECL address_v6 network() const ASIO_NOEXCEPT; + + /// Obtain an address range corresponding to the hosts in the network. + ASIO_DECL address_v6_range hosts() const ASIO_NOEXCEPT; + + /// Obtain the true network address, omitting any host bits. + network_v6 canonical() const ASIO_NOEXCEPT + { + return network_v6(network(), prefix_length()); + } + + /// Test if network is a valid host address. + bool is_host() const ASIO_NOEXCEPT + { + return prefix_length_ == 128; + } + + /// Test if a network is a real subnet of another network. + ASIO_DECL bool is_subnet_of(const network_v6& other) const; + + /// Get the network as an address in dotted decimal format. + ASIO_DECL std::string to_string() const; + + /// Get the network as an address in dotted decimal format. + ASIO_DECL std::string to_string(asio::error_code& ec) const; + + /// Compare two networks for equality. + friend bool operator==(const network_v6& a, const network_v6& b) + { + return a.address_ == b.address_ && a.prefix_length_ == b.prefix_length_; + } + + /// Compare two networks for inequality. + friend bool operator!=(const network_v6& a, const network_v6& b) + { + return !(a == b); + } + +private: + address_v6 address_; + unsigned short prefix_length_; +}; + +/// Create an IPv6 network from an address and prefix length. +/** + * @relates address_v6 + */ +inline network_v6 make_network_v6( + const address_v6& addr, unsigned short prefix_len) +{ + return network_v6(addr, prefix_len); +} + +/// Create an IPv6 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v6 + */ +ASIO_DECL network_v6 make_network_v6(const char* str); + +/// Create an IPv6 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v6 + */ +ASIO_DECL network_v6 make_network_v6( + const char* str, asio::error_code& ec); + +/// Create an IPv6 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v6 + */ +ASIO_DECL network_v6 make_network_v6(const std::string& str); + +/// Create an IPv6 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v6 + */ +ASIO_DECL network_v6 make_network_v6( + const std::string& str, asio::error_code& ec); + +#if defined(ASIO_HAS_STRING_VIEW) \ + || defined(GENERATING_DOCUMENTATION) + +/// Create an IPv6 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v6 + */ +ASIO_DECL network_v6 make_network_v6(string_view str); + +/// Create an IPv6 network from a string containing IP address and prefix +/// length. +/** + * @relates network_v6 + */ +ASIO_DECL network_v6 make_network_v6( + string_view str, asio::error_code& ec); + +#endif // defined(ASIO_HAS_STRING_VIEW) + // || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_NO_IOSTREAM) + +/// Output a network as a string. +/** + * Used to output a human-readable string for a specified network. + * + * @param os The output stream to which the string will be written. + * + * @param net The network to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v6 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const network_v6& net); + +#endif // !defined(ASIO_NO_IOSTREAM) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/impl/network_v6.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/network_v6.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_NETWORK_V6_HPP diff --git a/subprojects/asio/include/asio/ip/resolver_base.hpp b/subprojects/asio/include/asio/ip/resolver_base.hpp new file mode 100644 index 000000000..d32c9110d --- /dev/null +++ b/subprojects/asio/include/asio/ip/resolver_base.hpp @@ -0,0 +1,129 @@ +// +// ip/resolver_base.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_RESOLVER_BASE_HPP +#define ASIO_IP_RESOLVER_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// The resolver_base class is used as a base for the basic_resolver class +/// templates to provide a common place to define the flag constants. +class resolver_base +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// A bitmask type (C++ Std [lib.bitmask.types]). + typedef unspecified flags; + + /// Determine the canonical name of the host specified in the query. + static const flags canonical_name = implementation_defined; + + /// Indicate that returned endpoint is intended for use as a locally bound + /// socket endpoint. + static const flags passive = implementation_defined; + + /// Host name should be treated as a numeric string defining an IPv4 or IPv6 + /// address and no name resolution should be attempted. + static const flags numeric_host = implementation_defined; + + /// Service name should be treated as a numeric string defining a port number + /// and no name resolution should be attempted. + static const flags numeric_service = implementation_defined; + + /// If the query protocol family is specified as IPv6, return IPv4-mapped + /// IPv6 addresses on finding no IPv6 addresses. + static const flags v4_mapped = implementation_defined; + + /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses. + static const flags all_matching = implementation_defined; + + /// Only return IPv4 addresses if a non-loopback IPv4 address is configured + /// for the system. Only return IPv6 addresses if a non-loopback IPv6 address + /// is configured for the system. + static const flags address_configured = implementation_defined; +#else + enum flags + { + canonical_name = ASIO_OS_DEF(AI_CANONNAME), + passive = ASIO_OS_DEF(AI_PASSIVE), + numeric_host = ASIO_OS_DEF(AI_NUMERICHOST), + numeric_service = ASIO_OS_DEF(AI_NUMERICSERV), + v4_mapped = ASIO_OS_DEF(AI_V4MAPPED), + all_matching = ASIO_OS_DEF(AI_ALL), + address_configured = ASIO_OS_DEF(AI_ADDRCONFIG) + }; + + // Implement bitmask operations as shown in C++ Std [lib.bitmask.types]. + + friend flags operator&(flags x, flags y) + { + return static_cast( + static_cast(x) & static_cast(y)); + } + + friend flags operator|(flags x, flags y) + { + return static_cast( + static_cast(x) | static_cast(y)); + } + + friend flags operator^(flags x, flags y) + { + return static_cast( + static_cast(x) ^ static_cast(y)); + } + + friend flags operator~(flags x) + { + return static_cast(~static_cast(x)); + } + + friend flags& operator&=(flags& x, flags y) + { + x = x & y; + return x; + } + + friend flags& operator|=(flags& x, flags y) + { + x = x | y; + return x; + } + + friend flags& operator^=(flags& x, flags y) + { + x = x ^ y; + return x; + } +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~resolver_base() + { + } +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_RESOLVER_BASE_HPP diff --git a/subprojects/asio/include/asio/ip/resolver_query_base.hpp b/subprojects/asio/include/asio/ip/resolver_query_base.hpp new file mode 100644 index 000000000..be368589e --- /dev/null +++ b/subprojects/asio/include/asio/ip/resolver_query_base.hpp @@ -0,0 +1,43 @@ +// +// ip/resolver_query_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_RESOLVER_QUERY_BASE_HPP +#define ASIO_IP_RESOLVER_QUERY_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ip/resolver_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// The resolver_query_base class is used as a base for the +/// basic_resolver_query class templates to provide a common place to define +/// the flag constants. +class resolver_query_base : public resolver_base +{ +protected: + /// Protected destructor to prevent deletion through this type. + ~resolver_query_base() + { + } +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_RESOLVER_QUERY_BASE_HPP diff --git a/subprojects/asio/include/asio/ip/resolver_service.hpp b/subprojects/asio/include/asio/ip/resolver_service.hpp new file mode 100644 index 000000000..519d72dbb --- /dev/null +++ b/subprojects/asio/include/asio/ip/resolver_service.hpp @@ -0,0 +1,200 @@ +// +// ip/resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_RESOLVER_SERVICE_HPP +#define ASIO_IP_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/async_result.hpp" +#include "asio/error_code.hpp" +#include "asio/io_context.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_resolver_service.hpp" +#else +# include "asio/detail/resolver_service.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Default service implementation for a resolver. +template +class resolver_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base< + resolver_service > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The protocol type. + typedef InternetProtocol protocol_type; + + /// The endpoint type. + typedef typename InternetProtocol::endpoint endpoint_type; + + /// The query type. + typedef basic_resolver_query query_type; + + /// The iterator type. + typedef basic_resolver_iterator iterator_type; + + /// The results type. + typedef basic_resolver_results results_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_WINDOWS_RUNTIME) + typedef asio::detail::winrt_resolver_service + service_impl_type; +#else + typedef asio::detail::resolver_service + service_impl_type; +#endif + +public: + /// The type of a resolver implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new resolver service for the specified io_context. + explicit resolver_service(asio::io_context& io_context) + : asio::detail::service_base< + resolver_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new resolver implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new resolver implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another resolver implementation. + void move_assign(implementation_type& impl, + resolver_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a resolver implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Cancel pending asynchronous operations. + void cancel(implementation_type& impl) + { + service_impl_.cancel(impl); + } + + /// Resolve a query to a list of entries. + results_type resolve(implementation_type& impl, const query_type& query, + asio::error_code& ec) + { + return service_impl_.resolve(impl, query, ec); + } + + /// Asynchronously resolve a query to a list of entries. + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(implementation_type& impl, const query_type& query, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_resolve(impl, query, init.completion_handler); + + return init.result.get(); + } + + /// Resolve an endpoint to a list of entries. + results_type resolve(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + return service_impl_.resolve(impl, endpoint, ec); + } + + /// Asynchronously resolve an endpoint to a list of entries. + template + ASIO_INITFN_RESULT_TYPE(ResolveHandler, + void (asio::error_code, results_type)) + async_resolve(implementation_type& impl, const endpoint_type& endpoint, + ASIO_MOVE_ARG(ResolveHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_resolve(impl, endpoint, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // Perform any fork-related housekeeping. + void notify_fork(asio::io_context::fork_event event) + { + service_impl_.notify_fork(event); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_IP_RESOLVER_SERVICE_HPP diff --git a/subprojects/asio/include/asio/ip/tcp.hpp b/subprojects/asio/include/asio/ip/tcp.hpp new file mode 100644 index 000000000..f1adeb02c --- /dev/null +++ b/subprojects/asio/include/asio/ip/tcp.hpp @@ -0,0 +1,155 @@ +// +// ip/tcp.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_TCP_HPP +#define ASIO_IP_TCP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/detail/socket_option.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for TCP. +/** + * The asio::ip::tcp class contains flags necessary for TCP sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol, InternetProtocol. + */ +class tcp +{ +public: + /// The type of a TCP endpoint. + typedef basic_endpoint endpoint; + + /// Construct to represent the IPv4 TCP protocol. + static tcp v4() + { + return tcp(ASIO_OS_DEF(AF_INET)); + } + + /// Construct to represent the IPv6 TCP protocol. + static tcp v6() + { + return tcp(ASIO_OS_DEF(AF_INET6)); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return ASIO_OS_DEF(SOCK_STREAM); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return ASIO_OS_DEF(IPPROTO_TCP); + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The TCP socket type. + typedef basic_stream_socket socket; + + /// The TCP acceptor type. + typedef basic_socket_acceptor acceptor; + + /// The TCP resolver type. + typedef basic_resolver resolver; + +#if !defined(ASIO_NO_IOSTREAM) + /// The TCP iostream type. + typedef basic_socket_iostream iostream; +#endif // !defined(ASIO_NO_IOSTREAM) + + /// Socket option for disabling the Nagle algorithm. + /** + * Implements the IPPROTO_TCP/TCP_NODELAY socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::tcp::no_delay option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined no_delay; +#else + typedef asio::detail::socket_option::boolean< + ASIO_OS_DEF(IPPROTO_TCP), ASIO_OS_DEF(TCP_NODELAY)> no_delay; +#endif + + /// Compare two protocols for equality. + friend bool operator==(const tcp& p1, const tcp& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const tcp& p1, const tcp& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit tcp(int protocol_family) + : family_(protocol_family) + { + } + + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_TCP_HPP diff --git a/subprojects/asio/include/asio/ip/udp.hpp b/subprojects/asio/include/asio/ip/udp.hpp new file mode 100644 index 000000000..1c93f9b0a --- /dev/null +++ b/subprojects/asio/include/asio/ip/udp.hpp @@ -0,0 +1,111 @@ +// +// ip/udp.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_UDP_HPP +#define ASIO_IP_UDP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/basic_datagram_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for UDP. +/** + * The asio::ip::udp class contains flags necessary for UDP sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol, InternetProtocol. + */ +class udp +{ +public: + /// The type of a UDP endpoint. + typedef basic_endpoint endpoint; + + /// Construct to represent the IPv4 UDP protocol. + static udp v4() + { + return udp(ASIO_OS_DEF(AF_INET)); + } + + /// Construct to represent the IPv6 UDP protocol. + static udp v6() + { + return udp(ASIO_OS_DEF(AF_INET6)); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return ASIO_OS_DEF(SOCK_DGRAM); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return ASIO_OS_DEF(IPPROTO_UDP); + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The UDP socket type. + typedef basic_datagram_socket socket; + + /// The UDP resolver type. + typedef basic_resolver resolver; + + /// Compare two protocols for equality. + friend bool operator==(const udp& p1, const udp& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const udp& p1, const udp& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit udp(int protocol_family) + : family_(protocol_family) + { + } + + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_UDP_HPP diff --git a/subprojects/asio/include/asio/ip/unicast.hpp b/subprojects/asio/include/asio/ip/unicast.hpp new file mode 100644 index 000000000..14e3e489f --- /dev/null +++ b/subprojects/asio/include/asio/ip/unicast.hpp @@ -0,0 +1,70 @@ +// +// ip/unicast.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_UNICAST_HPP +#define ASIO_IP_UNICAST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/ip/detail/socket_option.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { +namespace unicast { + +/// Socket option for time-to-live associated with outgoing unicast packets. +/** + * Implements the IPPROTO_IP/IP_UNICAST_TTL socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::unicast::hops option(4); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::ip::unicast::hops option; + * socket.get_option(option); + * int ttl = option.value(); + * @endcode + * + * @par Concepts: + * GettableSocketOption, SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined hops; +#else +typedef asio::ip::detail::socket_option::unicast_hops< + ASIO_OS_DEF(IPPROTO_IP), + ASIO_OS_DEF(IP_TTL), + ASIO_OS_DEF(IPPROTO_IPV6), + ASIO_OS_DEF(IPV6_UNICAST_HOPS)> hops; +#endif + +} // namespace unicast +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_UNICAST_HPP diff --git a/subprojects/asio/include/asio/ip/v6_only.hpp b/subprojects/asio/include/asio/ip/v6_only.hpp new file mode 100644 index 000000000..ac7234edc --- /dev/null +++ b/subprojects/asio/include/asio/ip/v6_only.hpp @@ -0,0 +1,69 @@ +// +// ip/v6_only.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_V6_ONLY_HPP +#define ASIO_IP_V6_ONLY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/socket_option.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +/// Socket option for determining whether an IPv6 socket supports IPv6 +/// communication only. +/** + * Implements the IPPROTO_IPV6/IP_V6ONLY socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::v6_only option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::ip::v6_only option; + * socket.get_option(option); + * bool v6_only = option.value(); + * @endcode + * + * @par Concepts: + * GettableSocketOption, SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined v6_only; +#elif defined(IPV6_V6ONLY) +typedef asio::detail::socket_option::boolean< + IPPROTO_IPV6, IPV6_V6ONLY> v6_only; +#else +typedef asio::detail::socket_option::boolean< + asio::detail::custom_socket_option_level, + asio::detail::always_fail_option> v6_only; +#endif + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_V6_ONLY_HPP diff --git a/subprojects/asio/include/asio/is_executor.hpp b/subprojects/asio/include/asio/is_executor.hpp new file mode 100644 index 000000000..a1661ec46 --- /dev/null +++ b/subprojects/asio/include/asio/is_executor.hpp @@ -0,0 +1,46 @@ +// +// is_executor.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IS_EXECUTOR_HPP +#define ASIO_IS_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// The is_executor trait detects whether a type T meets the Executor type +/// requirements. +/** + * Class template @c is_executor is a UnaryTypeTrait that is derived from @c + * true_type if the type @c T meets the syntactic requirements for Executor, + * otherwise @c false_type. + */ +template +struct is_executor +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_executor +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IS_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/is_read_buffered.hpp b/subprojects/asio/include/asio/is_read_buffered.hpp new file mode 100644 index 000000000..c5a67b2da --- /dev/null +++ b/subprojects/asio/include/asio/is_read_buffered.hpp @@ -0,0 +1,59 @@ +// +// is_read_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IS_READ_BUFFERED_HPP +#define ASIO_IS_READ_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffered_stream_fwd.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail { + +template +char is_read_buffered_helper(buffered_stream* s); + +template +char is_read_buffered_helper(buffered_read_stream* s); + +struct is_read_buffered_big_type { char data[10]; }; +is_read_buffered_big_type is_read_buffered_helper(...); + +} // namespace detail + +/// The is_read_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of read data. +template +class is_read_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// read data. + static const bool value; +#else + ASIO_STATIC_CONSTANT(bool, + value = sizeof(detail::is_read_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IS_READ_BUFFERED_HPP diff --git a/subprojects/asio/include/asio/is_write_buffered.hpp b/subprojects/asio/include/asio/is_write_buffered.hpp new file mode 100644 index 000000000..e237dd6f4 --- /dev/null +++ b/subprojects/asio/include/asio/is_write_buffered.hpp @@ -0,0 +1,59 @@ +// +// is_write_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IS_WRITE_BUFFERED_HPP +#define ASIO_IS_WRITE_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/buffered_write_stream_fwd.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail { + +template +char is_write_buffered_helper(buffered_stream* s); + +template +char is_write_buffered_helper(buffered_write_stream* s); + +struct is_write_buffered_big_type { char data[10]; }; +is_write_buffered_big_type is_write_buffered_helper(...); + +} // namespace detail + +/// The is_write_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of written data. +template +class is_write_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// written data. + static const bool value; +#else + ASIO_STATIC_CONSTANT(bool, + value = sizeof(detail::is_write_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IS_WRITE_BUFFERED_HPP diff --git a/subprojects/asio/include/asio/local/basic_endpoint.hpp b/subprojects/asio/include/asio/local/basic_endpoint.hpp new file mode 100644 index 000000000..94e470a4b --- /dev/null +++ b/subprojects/asio/include/asio/local/basic_endpoint.hpp @@ -0,0 +1,239 @@ +// +// local/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Derived from a public domain implementation written by Daniel Casimiro. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_BASIC_ENDPOINT_HPP +#define ASIO_LOCAL_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/local/detail/endpoint.hpp" + +#if !defined(ASIO_NO_IOSTREAM) +# include +#endif // !defined(ASIO_NO_IOSTREAM) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { + +/// Describes an endpoint for a UNIX socket. +/** + * The asio::local::basic_endpoint class template describes an endpoint + * that may be associated with a particular UNIX socket. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef Protocol protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef asio::detail::socket_addr_type data_type; +#endif + + /// Default constructor. + basic_endpoint() + { + } + + /// Construct an endpoint using the specified path name. + basic_endpoint(const char* path_name) + : impl_(path_name) + { + } + + /// Construct an endpoint using the specified path name. + basic_endpoint(const std::string& path_name) + : impl_(path_name) + { + } + + /// Copy constructor. + basic_endpoint(const basic_endpoint& other) + : impl_(other.impl_) + { + } + +#if defined(ASIO_HAS_MOVE) + /// Move constructor. + basic_endpoint(basic_endpoint&& other) + : impl_(other.impl_) + { + } +#endif // defined(ASIO_HAS_MOVE) + + /// Assign from another endpoint. + basic_endpoint& operator=(const basic_endpoint& other) + { + impl_ = other.impl_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) + /// Move-assign from another endpoint. + basic_endpoint& operator=(basic_endpoint&& other) + { + impl_ = other.impl_; + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + return protocol_type(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return impl_.data(); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return impl_.data(); + } + + /// Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return impl_.size(); + } + + /// Set the underlying size of the endpoint in the native type. + void resize(std::size_t new_size) + { + impl_.resize(new_size); + } + + /// Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return impl_.capacity(); + } + + /// Get the path associated with the endpoint. + std::string path() const + { + return impl_.path(); + } + + /// Set the path associated with the endpoint. + void path(const char* p) + { + impl_.path(p); + } + + /// Set the path associated with the endpoint. + void path(const std::string& p) + { + impl_.path(p); + } + + /// Compare two endpoints for equality. + friend bool operator==(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.impl_ == e2.impl_; + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1.impl_ == e2.impl_); + } + + /// Compare endpoints for ordering. + friend bool operator<(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.impl_ < e2.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator>(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e2.impl_ < e1.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator<=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e2 < e1); + } + + /// Compare endpoints for ordering. + friend bool operator>=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1 < e2); + } + +private: + // The underlying UNIX domain endpoint. + asio::local::detail::endpoint impl_; +}; + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates asio::local::basic_endpoint + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint) +{ + os << endpoint.path(); + return os; +} + +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_LOCAL_BASIC_ENDPOINT_HPP diff --git a/subprojects/asio/include/asio/local/connect_pair.hpp b/subprojects/asio/include/asio/local/connect_pair.hpp new file mode 100644 index 000000000..2f7c090ed --- /dev/null +++ b/subprojects/asio/include/asio/local/connect_pair.hpp @@ -0,0 +1,106 @@ +// +// local/connect_pair.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_CONNECT_PAIR_HPP +#define ASIO_LOCAL_CONNECT_PAIR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_socket.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/local/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { + +/// Create a pair of connected sockets. +template +void connect_pair( + basic_socket& socket1, + basic_socket& socket2); + +/// Create a pair of connected sockets. +template +ASIO_SYNC_OP_VOID connect_pair( + basic_socket& socket1, + basic_socket& socket2, + asio::error_code& ec); + +template +inline void connect_pair( + basic_socket& socket1, + basic_socket& socket2) +{ + asio::error_code ec; + connect_pair(socket1, socket2, ec); + asio::detail::throw_error(ec, "connect_pair"); +} + +template +inline ASIO_SYNC_OP_VOID connect_pair( + basic_socket& socket1, + basic_socket& socket2, + asio::error_code& ec) +{ + // Check that this function is only being used with a UNIX domain socket. + asio::local::basic_endpoint* tmp + = static_cast(0); + (void)tmp; + + Protocol protocol; + asio::detail::socket_type sv[2]; + if (asio::detail::socket_ops::socketpair(protocol.family(), + protocol.type(), protocol.protocol(), sv, ec) + == asio::detail::socket_error_retval) + ASIO_SYNC_OP_VOID_RETURN(ec); + + socket1.assign(protocol, sv[0], ec); + if (ec) + { + asio::error_code temp_ec; + asio::detail::socket_ops::state_type state[2] = { 0, 0 }; + asio::detail::socket_ops::close(sv[0], state[0], true, temp_ec); + asio::detail::socket_ops::close(sv[1], state[1], true, temp_ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + socket2.assign(protocol, sv[1], ec); + if (ec) + { + asio::error_code temp_ec; + socket1.close(temp_ec); + asio::detail::socket_ops::state_type state = 0; + asio::detail::socket_ops::close(sv[1], state, true, temp_ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_LOCAL_CONNECT_PAIR_HPP diff --git a/subprojects/asio/include/asio/local/datagram_protocol.hpp b/subprojects/asio/include/asio/local/datagram_protocol.hpp new file mode 100644 index 000000000..b87df2e56 --- /dev/null +++ b/subprojects/asio/include/asio/local/datagram_protocol.hpp @@ -0,0 +1,80 @@ +// +// local/datagram_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP +#define ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_datagram_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/local/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { + +/// Encapsulates the flags needed for datagram-oriented UNIX sockets. +/** + * The asio::local::datagram_protocol class contains flags necessary for + * datagram-oriented UNIX domain sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class datagram_protocol +{ +public: + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_DGRAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return 0; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return AF_UNIX; + } + + /// The type of a UNIX domain endpoint. + typedef basic_endpoint endpoint; + + /// The UNIX domain socket type. + typedef basic_datagram_socket socket; +}; + +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP diff --git a/subprojects/asio/include/asio/local/detail/endpoint.hpp b/subprojects/asio/include/asio/local/detail/endpoint.hpp new file mode 100644 index 000000000..4870f3b9a --- /dev/null +++ b/subprojects/asio/include/asio/local/detail/endpoint.hpp @@ -0,0 +1,133 @@ +// +// local/detail/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Derived from a public domain implementation written by Daniel Casimiro. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_DETAIL_ENDPOINT_HPP +#define ASIO_LOCAL_DETAIL_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) + +#include +#include +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { +namespace detail { + +// Helper class for implementing a UNIX domain endpoint. +class endpoint +{ +public: + // Default constructor. + ASIO_DECL endpoint(); + + // Construct an endpoint using the specified path name. + ASIO_DECL endpoint(const char* path_name); + + // Construct an endpoint using the specified path name. + ASIO_DECL endpoint(const std::string& path_name); + + // Copy constructor. + endpoint(const endpoint& other) + : data_(other.data_), + path_length_(other.path_length_) + { + } + + // Assign from another endpoint. + endpoint& operator=(const endpoint& other) + { + data_ = other.data_; + path_length_ = other.path_length_; + return *this; + } + + // Get the underlying endpoint in the native type. + asio::detail::socket_addr_type* data() + { + return &data_.base; + } + + // Get the underlying endpoint in the native type. + const asio::detail::socket_addr_type* data() const + { + return &data_.base; + } + + // Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return path_length_ + + offsetof(asio::detail::sockaddr_un_type, sun_path); + } + + // Set the underlying size of the endpoint in the native type. + ASIO_DECL void resize(std::size_t size); + + // Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return sizeof(asio::detail::sockaddr_un_type); + } + + // Get the path associated with the endpoint. + ASIO_DECL std::string path() const; + + // Set the path associated with the endpoint. + ASIO_DECL void path(const char* p); + + // Set the path associated with the endpoint. + ASIO_DECL void path(const std::string& p); + + // Compare two endpoints for equality. + ASIO_DECL friend bool operator==( + const endpoint& e1, const endpoint& e2); + + // Compare endpoints for ordering. + ASIO_DECL friend bool operator<( + const endpoint& e1, const endpoint& e2); + +private: + // The underlying UNIX socket address. + union data_union + { + asio::detail::socket_addr_type base; + asio::detail::sockaddr_un_type local; + } data_; + + // The length of the path associated with the endpoint. + std::size_t path_length_; + + // Initialise with a specified path. + ASIO_DECL void init(const char* path, std::size_t path_length); +}; + +} // namespace detail +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/local/detail/impl/endpoint.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + +#endif // ASIO_LOCAL_DETAIL_ENDPOINT_HPP diff --git a/subprojects/asio/include/asio/local/detail/impl/endpoint.ipp b/subprojects/asio/include/asio/local/detail/impl/endpoint.ipp new file mode 100644 index 000000000..d5bbf503c --- /dev/null +++ b/subprojects/asio/include/asio/local/detail/impl/endpoint.ipp @@ -0,0 +1,129 @@ +// +// local/detail/impl/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Derived from a public domain implementation written by Daniel Casimiro. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP +#define ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) + +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/local/detail/endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { +namespace detail { + +endpoint::endpoint() +{ + init("", 0); +} + +endpoint::endpoint(const char* path_name) +{ + using namespace std; // For strlen. + init(path_name, strlen(path_name)); +} + +endpoint::endpoint(const std::string& path_name) +{ + init(path_name.data(), path_name.length()); +} + +void endpoint::resize(std::size_t new_size) +{ + if (new_size > sizeof(asio::detail::sockaddr_un_type)) + { + asio::error_code ec(asio::error::invalid_argument); + asio::detail::throw_error(ec); + } + else if (new_size == 0) + { + path_length_ = 0; + } + else + { + path_length_ = new_size + - offsetof(asio::detail::sockaddr_un_type, sun_path); + + // The path returned by the operating system may be NUL-terminated. + if (path_length_ > 0 && data_.local.sun_path[path_length_ - 1] == 0) + --path_length_; + } +} + +std::string endpoint::path() const +{ + return std::string(data_.local.sun_path, path_length_); +} + +void endpoint::path(const char* p) +{ + using namespace std; // For strlen. + init(p, strlen(p)); +} + +void endpoint::path(const std::string& p) +{ + init(p.data(), p.length()); +} + +bool operator==(const endpoint& e1, const endpoint& e2) +{ + return e1.path() == e2.path(); +} + +bool operator<(const endpoint& e1, const endpoint& e2) +{ + return e1.path() < e2.path(); +} + +void endpoint::init(const char* path_name, std::size_t path_length) +{ + if (path_length > sizeof(data_.local.sun_path) - 1) + { + // The buffer is not large enough to store this address. + asio::error_code ec(asio::error::name_too_long); + asio::detail::throw_error(ec); + } + + using namespace std; // For memcpy. + data_.local = asio::detail::sockaddr_un_type(); + data_.local.sun_family = AF_UNIX; + if (path_length > 0) + memcpy(data_.local.sun_path, path_name, path_length); + path_length_ = path_length; + + // NUL-terminate normal path names. Names that start with a NUL are in the + // UNIX domain protocol's "abstract namespace" and are not NUL-terminated. + if (path_length > 0 && data_.local.sun_path[0] == 0) + data_.local.sun_path[path_length] = 0; +} + +} // namespace detail +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + +#endif // ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP diff --git a/subprojects/asio/include/asio/local/stream_protocol.hpp b/subprojects/asio/include/asio/local/stream_protocol.hpp new file mode 100644 index 000000000..e2ef5f3b9 --- /dev/null +++ b/subprojects/asio/include/asio/local/stream_protocol.hpp @@ -0,0 +1,90 @@ +// +// local/stream_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_STREAM_PROTOCOL_HPP +#define ASIO_LOCAL_STREAM_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/local/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { + +/// Encapsulates the flags needed for stream-oriented UNIX sockets. +/** + * The asio::local::stream_protocol class contains flags necessary for + * stream-oriented UNIX domain sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class stream_protocol +{ +public: + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_STREAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return 0; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return AF_UNIX; + } + + /// The type of a UNIX domain endpoint. + typedef basic_endpoint endpoint; + + /// The UNIX domain socket type. + typedef basic_stream_socket socket; + + /// The UNIX domain acceptor type. + typedef basic_socket_acceptor acceptor; + +#if !defined(ASIO_NO_IOSTREAM) + /// The UNIX domain iostream type. + typedef basic_socket_iostream iostream; +#endif // !defined(ASIO_NO_IOSTREAM) +}; + +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_LOCAL_STREAM_PROTOCOL_HPP diff --git a/subprojects/asio/include/asio/packaged_task.hpp b/subprojects/asio/include/asio/packaged_task.hpp new file mode 100644 index 000000000..29b15b880 --- /dev/null +++ b/subprojects/asio/include/asio/packaged_task.hpp @@ -0,0 +1,126 @@ +// +// packaged_task.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_PACKAGED_TASK_HPP +#define ASIO_PACKAGED_TASK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/future.hpp" + +#if defined(ASIO_HAS_STD_FUTURE_CLASS) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + +/// Partial specialisation of @c async_result for @c std::packaged_task. +template +class async_result, Signature> +{ +public: + /// The packaged task is the concrete completion handler type. + typedef std::packaged_task completion_handler_type; + + /// The return type of the initiating function is the future obtained from + /// the packaged task. + typedef std::future return_type; + + /// The constructor extracts the future from the packaged task. + explicit async_result(completion_handler_type& h) + : future_(h.get_future()) + { + } + + /// Returns the packaged task's future. + return_type get() + { + return std::move(future_); + } + +private: + return_type future_; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +template +struct async_result, Signature> +{ + typedef std::packaged_task completion_handler_type; + typedef std::future return_type; + + explicit async_result(completion_handler_type& h) + : future_(h.get_future()) + { + } + + return_type get() + { + return std::move(future_); + } + +private: + return_type future_; +}; + +#define ASIO_PRIVATE_ASYNC_RESULT_DEF(n) \ + template \ + class async_result< \ + std::packaged_task, Signature> \ + { \ + public: \ + typedef std::packaged_task< \ + Result(ASIO_VARIADIC_TARGS(n))> \ + completion_handler_type; \ + \ + typedef std::future return_type; \ + \ + explicit async_result(completion_handler_type& h) \ + : future_(h.get_future()) \ + { \ + } \ + \ + return_type get() \ + { \ + return std::move(future_); \ + } \ + \ + private: \ + return_type future_; \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ASYNC_RESULT_DEF) +#undef ASIO_PRIVATE_ASYNC_RESULT_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_FUTURE_CLASS) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_PACKAGED_TASK_HPP diff --git a/subprojects/asio/include/asio/placeholders.hpp b/subprojects/asio/include/asio/placeholders.hpp new file mode 100644 index 000000000..e71a21e7f --- /dev/null +++ b/subprojects/asio/include/asio/placeholders.hpp @@ -0,0 +1,151 @@ +// +// placeholders.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_PLACEHOLDERS_HPP +#define ASIO_PLACEHOLDERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_BIND) +# include +#endif // defined(ASIO_HAS_BOOST_BIND) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace placeholders { + +#if defined(GENERATING_DOCUMENTATION) + +/// An argument placeholder, for use with boost::bind(), that corresponds to +/// the error argument of a handler for any of the asynchronous functions. +unspecified error; + +/// An argument placeholder, for use with boost::bind(), that corresponds to +/// the bytes_transferred argument of a handler for asynchronous functions such +/// as asio::basic_stream_socket::async_write_some or +/// asio::async_write. +unspecified bytes_transferred; + +/// An argument placeholder, for use with boost::bind(), that corresponds to +/// the iterator argument of a handler for asynchronous functions such as +/// asio::async_connect. +unspecified iterator; + +/// An argument placeholder, for use with boost::bind(), that corresponds to +/// the results argument of a handler for asynchronous functions such as +/// asio::basic_resolver::async_resolve. +unspecified results; + +/// An argument placeholder, for use with boost::bind(), that corresponds to +/// the results argument of a handler for asynchronous functions such as +/// asio::async_connect. +unspecified endpoint; + +/// An argument placeholder, for use with boost::bind(), that corresponds to +/// the signal_number argument of a handler for asynchronous functions such as +/// asio::signal_set::async_wait. +unspecified signal_number; + +#elif defined(ASIO_HAS_BOOST_BIND) +# if defined(__BORLANDC__) || defined(__GNUC__) + +inline boost::arg<1> error() +{ + return boost::arg<1>(); +} + +inline boost::arg<2> bytes_transferred() +{ + return boost::arg<2>(); +} + +inline boost::arg<2> iterator() +{ + return boost::arg<2>(); +} + +inline boost::arg<2> results() +{ + return boost::arg<2>(); +} + +inline boost::arg<2> endpoint() +{ + return boost::arg<2>(); +} + +inline boost::arg<2> signal_number() +{ + return boost::arg<2>(); +} + +# else + +namespace detail +{ + template + struct placeholder + { + static boost::arg& get() + { + static boost::arg result; + return result; + } + }; +} + +# if defined(ASIO_MSVC) && (ASIO_MSVC < 1400) + +static boost::arg<1>& error + = asio::placeholders::detail::placeholder<1>::get(); +static boost::arg<2>& bytes_transferred + = asio::placeholders::detail::placeholder<2>::get(); +static boost::arg<2>& iterator + = asio::placeholders::detail::placeholder<2>::get(); +static boost::arg<2>& results + = asio::placeholders::detail::placeholder<2>::get(); +static boost::arg<2>& endpoint + = asio::placeholders::detail::placeholder<2>::get(); +static boost::arg<2>& signal_number + = asio::placeholders::detail::placeholder<2>::get(); + +# else + +namespace +{ + boost::arg<1>& error + = asio::placeholders::detail::placeholder<1>::get(); + boost::arg<2>& bytes_transferred + = asio::placeholders::detail::placeholder<2>::get(); + boost::arg<2>& iterator + = asio::placeholders::detail::placeholder<2>::get(); + boost::arg<2>& results + = asio::placeholders::detail::placeholder<2>::get(); + boost::arg<2>& endpoint + = asio::placeholders::detail::placeholder<2>::get(); + boost::arg<2>& signal_number + = asio::placeholders::detail::placeholder<2>::get(); +} // namespace + +# endif +# endif +#endif + +} // namespace placeholders +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_PLACEHOLDERS_HPP diff --git a/subprojects/asio/include/asio/posix/basic_descriptor.hpp b/subprojects/asio/include/asio/posix/basic_descriptor.hpp new file mode 100644 index 000000000..c15da632c --- /dev/null +++ b/subprojects/asio/include/asio/posix/basic_descriptor.hpp @@ -0,0 +1,582 @@ +// +// posix/basic_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_POSIX_BASIC_DESCRIPTOR_HPP +#define ASIO_POSIX_BASIC_DESCRIPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/posix/descriptor_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace posix { + +/// Provides POSIX descriptor functionality. +/** + * The posix::basic_descriptor class template provides the ability to wrap a + * POSIX descriptor. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_descriptor + : public basic_io_object, + public descriptor_base +{ +public: + /// The native representation of a descriptor. + typedef typename DescriptorService::native_handle_type native_handle_type; + + /// A basic_descriptor is always the lowest layer. + typedef basic_descriptor lowest_layer_type; + + /// Construct a basic_descriptor without opening it. + /** + * This constructor creates a descriptor without opening it. + * + * @param io_context The io_context object that the descriptor will use to + * dispatch handlers for any asynchronous operations performed on the + * descriptor. + */ + explicit basic_descriptor(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct a basic_descriptor on an existing native descriptor. + /** + * This constructor creates a descriptor object to hold an existing native + * descriptor. + * + * @param io_context The io_context object that the descriptor will use to + * dispatch handlers for any asynchronous operations performed on the + * descriptor. + * + * @param native_descriptor A native descriptor. + * + * @throws asio::system_error Thrown on failure. + */ + basic_descriptor(asio::io_context& io_context, + const native_handle_type& native_descriptor) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_descriptor, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_descriptor from another. + /** + * This constructor moves a descriptor from one object to another. + * + * @param other The other basic_descriptor object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_descriptor(io_context&) constructor. + */ + basic_descriptor(basic_descriptor&& other) + : basic_io_object( + ASIO_MOVE_CAST(basic_descriptor)(other)) + { + } + + /// Move-assign a basic_descriptor from another. + /** + * This assignment operator moves a descriptor from one object to another. + * + * @param other The other basic_descriptor object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_descriptor(io_context&) constructor. + */ + basic_descriptor& operator=(basic_descriptor&& other) + { + basic_io_object::operator=( + ASIO_MOVE_CAST(basic_descriptor)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_descriptor cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_descriptor cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Assign an existing native descriptor to the descriptor. + /* + * This function opens the descriptor to hold an existing native descriptor. + * + * @param native_descriptor A native descriptor. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& native_descriptor) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_descriptor, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native descriptor to the descriptor. + /* + * This function opens the descriptor to hold an existing native descriptor. + * + * @param native_descriptor A native descriptor. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& native_descriptor, + asio::error_code& ec) + { + this->get_service().assign( + this->get_implementation(), native_descriptor, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the descriptor is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the descriptor. + /** + * This function is used to close the descriptor. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the descriptor. + /** + * This function is used to close the descriptor. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native descriptor representation. + /** + * This function may be used to obtain the underlying representation of the + * descriptor. This is intended to allow access to native descriptor + * functionality that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Release ownership of the native descriptor implementation. + /** + * This function may be used to obtain the underlying representation of the + * descriptor. After calling this function, @c is_open() returns false. The + * caller is responsible for closing the descriptor. + * + * All outstanding asynchronous read or write operations will finish + * immediately, and the handlers for cancelled operations will be passed the + * asio::error::operation_aborted error. + */ + native_handle_type release() + { + return this->get_service().release(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the descriptor. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the descriptor. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the descriptor. + /** + * This function is used to execute an IO control command on the descriptor. + * + * @param command The IO control command to be performed on the descriptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::posix::descriptor_base::bytes_readable @n + * asio::posix::descriptor_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * asio::posix::stream_descriptor::bytes_readable command; + * descriptor.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + this->get_service().io_control(this->get_implementation(), command, ec); + asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the descriptor. + /** + * This function is used to execute an IO control command on the descriptor. + * + * @param command The IO control command to be performed on the descriptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::posix::descriptor_base::bytes_readable @n + * asio::posix::descriptor_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * asio::posix::stream_descriptor::bytes_readable command; + * asio::error_code ec; + * descriptor.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + asio::error_code& ec) + { + this->get_service().io_control(this->get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the descriptor. + /** + * @returns @c true if the descriptor's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + bool non_blocking() const + { + return this->get_service().non_blocking(this->get_implementation()); + } + + /// Sets the non-blocking mode of the descriptor. + /** + * @param mode If @c true, the descriptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @throws asio::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + void non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().non_blocking(this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the descriptor. + /** + * @param mode If @c true, the descriptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + ASIO_SYNC_OP_VOID non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().non_blocking(this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native descriptor implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native descriptor. This mode has no effect on the behaviour of the + * descriptor object's synchronous operations. + * + * @returns @c true if the underlying descriptor is in non-blocking mode and + * direct system calls may fail with asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the descriptor object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native descriptor. + */ + bool native_non_blocking() const + { + return this->get_service().native_non_blocking( + this->get_implementation()); + } + + /// Sets the non-blocking mode of the native descriptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native descriptor. It has no effect on the behaviour of the descriptor + * object's synchronous operations. + * + * @param mode If @c true, the underlying descriptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @throws asio::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + void native_non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native descriptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native descriptor. It has no effect on the behaviour of the descriptor + * object's synchronous operations. + * + * @param mode If @c true, the underlying descriptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the descriptor to become ready to read, ready to write, or to + /// have pending error conditions. + /** + * This function is used to perform a blocking wait for a descriptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired descriptor state. + * + * @par Example + * Waiting for a descriptor to become readable. + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * descriptor.wait(asio::posix::stream_descriptor::wait_read); + * @endcode + */ + void wait(wait_type w) + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), w, ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the descriptor to become ready to read, ready to write, or to + /// have pending error conditions. + /** + * This function is used to perform a blocking wait for a descriptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired descriptor state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for a descriptor to become readable. + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * asio::error_code ec; + * descriptor.wait(asio::posix::stream_descriptor::wait_read, ec); + * @endcode + */ + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the descriptor to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for a descriptor to + * enter a ready to read, write or error condition state. + * + * @param w Specifies the desired descriptor state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void wait_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * descriptor.async_wait( + * asio::posix::stream_descriptor::wait_read, + * wait_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(wait_type w, ASIO_MOVE_ARG(WaitHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + return this->get_service().async_wait(this->get_implementation(), + w, ASIO_MOVE_CAST(WaitHandler)(handler)); + } + +protected: + /// Protected destructor to prevent deletion through this type. + ~basic_descriptor() + { + } +}; + +} // namespace posix +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_POSIX_BASIC_DESCRIPTOR_HPP diff --git a/subprojects/asio/include/asio/posix/basic_stream_descriptor.hpp b/subprojects/asio/include/asio/posix/basic_stream_descriptor.hpp new file mode 100644 index 000000000..acd5cb6ae --- /dev/null +++ b/subprojects/asio/include/asio/posix/basic_stream_descriptor.hpp @@ -0,0 +1,362 @@ +// +// posix/basic_stream_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP +#define ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/posix/basic_descriptor.hpp" +#include "asio/posix/stream_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace posix { + +/// Provides stream-oriented descriptor functionality. +/** + * The posix::basic_stream_descriptor class template provides asynchronous and + * blocking stream-oriented descriptor functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class basic_stream_descriptor + : public basic_descriptor +{ +public: + /// The native representation of a descriptor. + typedef typename StreamDescriptorService::native_handle_type + native_handle_type; + + /// Construct a basic_stream_descriptor without opening it. + /** + * This constructor creates a stream descriptor without opening it. The + * descriptor needs to be opened and then connected or accepted before data + * can be sent or received on it. + * + * @param io_context The io_context object that the stream descriptor will + * use to dispatch handlers for any asynchronous operations performed on the + * descriptor. + */ + explicit basic_stream_descriptor(asio::io_context& io_context) + : basic_descriptor(io_context) + { + } + + /// Construct a basic_stream_descriptor on an existing native descriptor. + /** + * This constructor creates a stream descriptor object to hold an existing + * native descriptor. + * + * @param io_context The io_context object that the stream descriptor will + * use to dispatch handlers for any asynchronous operations performed on the + * descriptor. + * + * @param native_descriptor The new underlying descriptor implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_descriptor(asio::io_context& io_context, + const native_handle_type& native_descriptor) + : basic_descriptor(io_context, native_descriptor) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_stream_descriptor from another. + /** + * This constructor moves a stream descriptor from one object to another. + * + * @param other The other basic_stream_descriptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_descriptor(io_context&) constructor. + */ + basic_stream_descriptor(basic_stream_descriptor&& other) + : basic_descriptor( + ASIO_MOVE_CAST(basic_stream_descriptor)(other)) + { + } + + /// Move-assign a basic_stream_descriptor from another. + /** + * This assignment operator moves a stream descriptor from one object to + * another. + * + * @param other The other basic_stream_descriptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_descriptor(io_context&) constructor. + */ + basic_stream_descriptor& operator=(basic_stream_descriptor&& other) + { + basic_descriptor::operator=( + ASIO_MOVE_CAST(basic_stream_descriptor)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Write some data to the descriptor. + /** + * This function is used to write data to the stream descriptor. The function + * call will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the descriptor. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the descriptor. + /** + * This function is used to write data to the stream descriptor. The function + * call will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the descriptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().write_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream + * descriptor. The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the descriptor. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + return this->get_service().async_write_some(this->get_implementation(), + buffers, ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Read some data from the descriptor. + /** + * This function is used to read data from the stream descriptor. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the descriptor. + /** + * This function is used to read data from the stream descriptor. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().read_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream + * descriptor. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + return this->get_service().async_read_some(this->get_implementation(), + buffers, ASIO_MOVE_CAST(ReadHandler)(handler)); + } +}; + +} // namespace posix +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP diff --git a/subprojects/asio/include/asio/posix/descriptor.hpp b/subprojects/asio/include/asio/posix/descriptor.hpp new file mode 100644 index 000000000..d0cee1a26 --- /dev/null +++ b/subprojects/asio/include/asio/posix/descriptor.hpp @@ -0,0 +1,644 @@ +// +// posix/descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_POSIX_DESCRIPTOR_HPP +#define ASIO_POSIX_DESCRIPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/async_result.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/posix/descriptor_base.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#define ASIO_SVC_T asio::detail::reactive_descriptor_service + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace posix { + +/// Provides POSIX descriptor functionality. +/** + * The posix::descriptor class template provides the ability to wrap a + * POSIX descriptor. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class descriptor + : ASIO_SVC_ACCESS basic_io_object, + public descriptor_base +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The native representation of a descriptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef ASIO_SVC_T::native_handle_type native_handle_type; +#endif + + /// A descriptor is always the lowest layer. + typedef descriptor lowest_layer_type; + + /// Construct a descriptor without opening it. + /** + * This constructor creates a descriptor without opening it. + * + * @param io_context The io_context object that the descriptor will use to + * dispatch handlers for any asynchronous operations performed on the + * descriptor. + */ + explicit descriptor(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct a descriptor on an existing native descriptor. + /** + * This constructor creates a descriptor object to hold an existing native + * descriptor. + * + * @param io_context The io_context object that the descriptor will use to + * dispatch handlers for any asynchronous operations performed on the + * descriptor. + * + * @param native_descriptor A native descriptor. + * + * @throws asio::system_error Thrown on failure. + */ + descriptor(asio::io_context& io_context, + const native_handle_type& native_descriptor) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_descriptor, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a descriptor from another. + /** + * This constructor moves a descriptor from one object to another. + * + * @param other The other descriptor object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c descriptor(io_context&) constructor. + */ + descriptor(descriptor&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign a descriptor from another. + /** + * This assignment operator moves a descriptor from one object to another. + * + * @param other The other descriptor object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c descriptor(io_context&) constructor. + */ + descriptor& operator=(descriptor&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a descriptor cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a descriptor cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Assign an existing native descriptor to the descriptor. + /* + * This function opens the descriptor to hold an existing native descriptor. + * + * @param native_descriptor A native descriptor. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& native_descriptor) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_descriptor, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native descriptor to the descriptor. + /* + * This function opens the descriptor to hold an existing native descriptor. + * + * @param native_descriptor A native descriptor. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& native_descriptor, + asio::error_code& ec) + { + this->get_service().assign( + this->get_implementation(), native_descriptor, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the descriptor is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the descriptor. + /** + * This function is used to close the descriptor. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the descriptor. + /** + * This function is used to close the descriptor. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native descriptor representation. + /** + * This function may be used to obtain the underlying representation of the + * descriptor. This is intended to allow access to native descriptor + * functionality that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Release ownership of the native descriptor implementation. + /** + * This function may be used to obtain the underlying representation of the + * descriptor. After calling this function, @c is_open() returns false. The + * caller is responsible for closing the descriptor. + * + * All outstanding asynchronous read or write operations will finish + * immediately, and the handlers for cancelled operations will be passed the + * asio::error::operation_aborted error. + */ + native_handle_type release() + { + return this->get_service().release(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the descriptor. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the descriptor. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the descriptor. + /** + * This function is used to execute an IO control command on the descriptor. + * + * @param command The IO control command to be performed on the descriptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::posix::descriptor_base::bytes_readable @n + * asio::posix::descriptor_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * asio::posix::stream_descriptor::bytes_readable command; + * descriptor.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + this->get_service().io_control(this->get_implementation(), command, ec); + asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the descriptor. + /** + * This function is used to execute an IO control command on the descriptor. + * + * @param command The IO control command to be performed on the descriptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::posix::descriptor_base::bytes_readable @n + * asio::posix::descriptor_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * asio::posix::stream_descriptor::bytes_readable command; + * asio::error_code ec; + * descriptor.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + asio::error_code& ec) + { + this->get_service().io_control(this->get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the descriptor. + /** + * @returns @c true if the descriptor's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + bool non_blocking() const + { + return this->get_service().non_blocking(this->get_implementation()); + } + + /// Sets the non-blocking mode of the descriptor. + /** + * @param mode If @c true, the descriptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @throws asio::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + void non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().non_blocking(this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the descriptor. + /** + * @param mode If @c true, the descriptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + ASIO_SYNC_OP_VOID non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().non_blocking(this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native descriptor implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native descriptor. This mode has no effect on the behaviour of the + * descriptor object's synchronous operations. + * + * @returns @c true if the underlying descriptor is in non-blocking mode and + * direct system calls may fail with asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the descriptor object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native descriptor. + */ + bool native_non_blocking() const + { + return this->get_service().native_non_blocking( + this->get_implementation()); + } + + /// Sets the non-blocking mode of the native descriptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native descriptor. It has no effect on the behaviour of the descriptor + * object's synchronous operations. + * + * @param mode If @c true, the underlying descriptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @throws asio::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + void native_non_blocking(bool mode) + { + asio::error_code ec; + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native descriptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native descriptor. It has no effect on the behaviour of the descriptor + * object's synchronous operations. + * + * @param mode If @c true, the underlying descriptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, asio::error_code& ec) + { + this->get_service().native_non_blocking( + this->get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the descriptor to become ready to read, ready to write, or to + /// have pending error conditions. + /** + * This function is used to perform a blocking wait for a descriptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired descriptor state. + * + * @par Example + * Waiting for a descriptor to become readable. + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * descriptor.wait(asio::posix::stream_descriptor::wait_read); + * @endcode + */ + void wait(wait_type w) + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), w, ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the descriptor to become ready to read, ready to write, or to + /// have pending error conditions. + /** + * This function is used to perform a blocking wait for a descriptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired descriptor state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for a descriptor to become readable. + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * asio::error_code ec; + * descriptor.wait(asio::posix::stream_descriptor::wait_read, ec); + * @endcode + */ + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the descriptor to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for a descriptor to + * enter a ready to read, write or error condition state. + * + * @param w Specifies the desired descriptor state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * @code + * void wait_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * descriptor.async_wait( + * asio::posix::stream_descriptor::wait_read, + * wait_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(wait_type w, ASIO_MOVE_ARG(WaitHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + async_completion init(handler); + + this->get_service().async_wait( + this->get_implementation(), w, init.completion_handler); + + return init.result.get(); + } + +protected: + /// Protected destructor to prevent deletion through this type. + /** + * This function destroys the descriptor, cancelling any outstanding + * asynchronous wait operations associated with the descriptor as if by + * calling @c cancel. + */ + ~descriptor() + { + } +}; + +} // namespace posix +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#undef ASIO_SVC_T + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_POSIX_DESCRIPTOR_HPP diff --git a/subprojects/asio/include/asio/posix/descriptor_base.hpp b/subprojects/asio/include/asio/posix/descriptor_base.hpp new file mode 100644 index 000000000..4aac04f10 --- /dev/null +++ b/subprojects/asio/include/asio/posix/descriptor_base.hpp @@ -0,0 +1,90 @@ +// +// posix/descriptor_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_POSIX_DESCRIPTOR_BASE_HPP +#define ASIO_POSIX_DESCRIPTOR_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/io_control.hpp" +#include "asio/detail/socket_option.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace posix { + +/// The descriptor_base class is used as a base for the descriptor class as a +/// place to define the associated IO control commands. +class descriptor_base +{ +public: + /// Wait types. + /** + * For use with descriptor::wait() and descriptor::async_wait(). + */ + enum wait_type + { + /// Wait for a descriptor to become ready to read. + wait_read, + + /// Wait for a descriptor to become ready to write. + wait_write, + + /// Wait for a descriptor to have error conditions pending. + wait_error + }; + + /// IO control command to get the amount of data that can be read without + /// blocking. + /** + * Implements the FIONREAD IO control command. + * + * @par Example + * @code + * asio::posix::stream_descriptor descriptor(io_context); + * ... + * asio::descriptor_base::bytes_readable command(true); + * descriptor.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + * + * @par Concepts: + * IoControlCommand. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined bytes_readable; +#else + typedef asio::detail::io_control::bytes_readable bytes_readable; +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~descriptor_base() + { + } +}; + +} // namespace posix +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_POSIX_DESCRIPTOR_BASE_HPP diff --git a/subprojects/asio/include/asio/posix/stream_descriptor.hpp b/subprojects/asio/include/asio/posix/stream_descriptor.hpp new file mode 100644 index 000000000..abb426a39 --- /dev/null +++ b/subprojects/asio/include/asio/posix/stream_descriptor.hpp @@ -0,0 +1,360 @@ +// +// posix/stream_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_POSIX_STREAM_DESCRIPTOR_HPP +#define ASIO_POSIX_STREAM_DESCRIPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/posix/descriptor.hpp" + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/posix/basic_stream_descriptor.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +namespace asio { +namespace posix { + +#if defined(ASIO_ENABLE_OLD_SERVICES) +// Typedef for the typical usage of a stream-oriented descriptor. +typedef basic_stream_descriptor<> stream_descriptor; +#else // defined(ASIO_ENABLE_OLD_SERVICES) +/// Provides stream-oriented descriptor functionality. +/** + * The posix::stream_descriptor class template provides asynchronous and + * blocking stream-oriented descriptor functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +class stream_descriptor + : public descriptor +{ +public: + /// Construct a stream_descriptor without opening it. + /** + * This constructor creates a stream descriptor without opening it. The + * descriptor needs to be opened and then connected or accepted before data + * can be sent or received on it. + * + * @param io_context The io_context object that the stream descriptor will + * use to dispatch handlers for any asynchronous operations performed on the + * descriptor. + */ + explicit stream_descriptor(asio::io_context& io_context) + : descriptor(io_context) + { + } + + /// Construct a stream_descriptor on an existing native descriptor. + /** + * This constructor creates a stream descriptor object to hold an existing + * native descriptor. + * + * @param io_context The io_context object that the stream descriptor will + * use to dispatch handlers for any asynchronous operations performed on the + * descriptor. + * + * @param native_descriptor The new underlying descriptor implementation. + * + * @throws asio::system_error Thrown on failure. + */ + stream_descriptor(asio::io_context& io_context, + const native_handle_type& native_descriptor) + : descriptor(io_context, native_descriptor) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a stream_descriptor from another. + /** + * This constructor moves a stream descriptor from one object to another. + * + * @param other The other stream_descriptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c stream_descriptor(io_context&) constructor. + */ + stream_descriptor(stream_descriptor&& other) + : descriptor(std::move(other)) + { + } + + /// Move-assign a stream_descriptor from another. + /** + * This assignment operator moves a stream descriptor from one object to + * another. + * + * @param other The other stream_descriptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c stream_descriptor(io_context&) constructor. + */ + stream_descriptor& operator=(stream_descriptor&& other) + { + descriptor::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Write some data to the descriptor. + /** + * This function is used to write data to the stream descriptor. The function + * call will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the descriptor. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the descriptor. + /** + * This function is used to write data to the stream descriptor. The function + * call will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the descriptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().write_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream + * descriptor. The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the descriptor. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + asio::async_completion init(handler); + + this->get_service().async_write_some( + this->get_implementation(), buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the descriptor. + /** + * This function is used to read data from the stream descriptor. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the descriptor. + /** + * This function is used to read data from the stream descriptor. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().read_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream + * descriptor. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + asio::async_completion init(handler); + + this->get_service().async_read_some( + this->get_implementation(), buffers, init.completion_handler); + + return init.result.get(); + } +}; +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +} // namespace posix +} // namespace asio + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_POSIX_STREAM_DESCRIPTOR_HPP diff --git a/subprojects/asio/include/asio/posix/stream_descriptor_service.hpp b/subprojects/asio/include/asio/posix/stream_descriptor_service.hpp new file mode 100644 index 000000000..4ffbb9602 --- /dev/null +++ b/subprojects/asio/include/asio/posix/stream_descriptor_service.hpp @@ -0,0 +1,279 @@ +// +// posix/stream_descriptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP +#define ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/async_result.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace posix { + +/// Default service implementation for a stream descriptor. +class stream_descriptor_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + +private: + // The type of the platform-specific implementation. + typedef detail::reactive_descriptor_service service_impl_type; + +public: + /// The type of a stream descriptor implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native descriptor type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new stream descriptor service for the specified io_context. + explicit stream_descriptor_service(asio::io_context& io_context) + : asio::detail::service_base(io_context), + service_impl_(io_context) + { + } + + /// Construct a new stream descriptor implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new stream descriptor implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another stream descriptor implementation. + void move_assign(implementation_type& impl, + stream_descriptor_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a stream descriptor implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Assign an existing native descriptor to a stream descriptor. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const native_handle_type& native_descriptor, + asio::error_code& ec) + { + service_impl_.assign(impl, native_descriptor, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the descriptor is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a stream descriptor implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native descriptor implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Release ownership of the native descriptor implementation. + native_handle_type release(implementation_type& impl) + { + return service_impl_.release(impl); + } + + /// Cancel all asynchronous operations associated with the descriptor. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the descriptor. + template + ASIO_SYNC_OP_VOID io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + service_impl_.io_control(impl, command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the descriptor. + bool non_blocking(const implementation_type& impl) const + { + return service_impl_.non_blocking(impl); + } + + /// Sets the non-blocking mode of the descriptor. + ASIO_SYNC_OP_VOID non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native descriptor implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return service_impl_.native_non_blocking(impl); + } + + /// Sets the non-blocking mode of the native descriptor implementation. + ASIO_SYNC_OP_VOID native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.native_non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the descriptor to become ready to read, ready to write, or to + /// have pending error conditions. + ASIO_SYNC_OP_VOID wait(implementation_type& impl, + descriptor_base::wait_type w, asio::error_code& ec) + { + service_impl_.wait(impl, w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the descriptor to become ready to read, ready to + /// write, or to have pending error conditions. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, descriptor_base::wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, w, init.completion_handler); + + return init.result.get(); + } + + /// Write the given data to the stream. + template + std::size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some(impl, buffers, ec); + } + + /// Start an asynchronous write. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_write_some(impl, buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the stream. + template + std::size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some(impl, buffers, ec); + } + + /// Start an asynchronous read. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_read_some(impl, buffers, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace posix +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP diff --git a/subprojects/asio/include/asio/post.hpp b/subprojects/asio/include/asio/post.hpp new file mode 100644 index 000000000..87d7b96d6 --- /dev/null +++ b/subprojects/asio/include/asio/post.hpp @@ -0,0 +1,107 @@ +// +// post.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_POST_HPP +#define ASIO_POST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the object's associated + * executor. The function object is queued for execution, and is never called + * from the current thread prior to returning from post(). + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex by performing + * get_associated_executor(handler). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Performs ex.post(std::move(handler), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post( + ASIO_MOVE_ARG(CompletionToken) token); + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the specified executor. + * The function object is queued for execution, and is never called from the + * current thread prior to returning from post(). + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex1 by performing + * get_associated_executor(handler). + * + * @li Creates a work object @c w by performing make_work(ex1). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Constructs a function object @c f with a function call operator that + * performs ex1.dispatch(std::move(handler), alloc) followed by + * w.reset(). + * + * @li Performs Executor(ex).post(std::move(f), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post( + const Executor& ex, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type* = 0); + +/// Submits a completion token or function object for execution. +/** + * @returns post(ctx.get_executor(), forward(token)). + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post( + ExecutionContext& ctx, ASIO_MOVE_ARG(CompletionToken) token, + typename enable_if::value>::type* = 0); + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/post.hpp" + +#endif // ASIO_POST_HPP diff --git a/subprojects/asio/include/asio/raw_socket_service.hpp b/subprojects/asio/include/asio/raw_socket_service.hpp new file mode 100644 index 000000000..2bccf032e --- /dev/null +++ b/subprojects/asio/include/asio/raw_socket_service.hpp @@ -0,0 +1,466 @@ +// +// raw_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_RAW_SOCKET_SERVICE_HPP +#define ASIO_RAW_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a raw socket. +template +class raw_socket_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_WINDOWS_RUNTIME) + typedef detail::null_socket_service service_impl_type; +#elif defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#else + typedef detail::reactive_socket_service service_impl_type; +#endif + +public: + /// The type of a raw socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new raw socket service for the specified io_context. + explicit raw_socket_service(asio::io_context& io_context) + : asio::detail::service_base< + raw_socket_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new raw socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new raw socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another raw socket implementation. + void move_assign(implementation_type& impl, + raw_socket_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } + + // All socket services have access to each other's implementations. + template friend class raw_socket_service; + + /// Move-construct a new raw socket implementation from another protocol + /// type. + template + void converting_move_construct(implementation_type& impl, + raw_socket_service& other_service, + typename raw_socket_service< + Protocol1>::implementation_type& other_impl, + typename enable_if::value>::type* = 0) + { + service_impl_.template converting_move_construct( + impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a raw socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + // Open a new raw socket implementation. + ASIO_SYNC_OP_VOID open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (protocol.type() == ASIO_OS_DEF(SOCK_RAW)) + service_impl_.open(impl, protocol, ec); + else + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to a raw socket. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + service_impl_.assign(impl, protocol, native_socket, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a raw socket implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying socket. + native_handle_type release(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.release(impl, ec); + } + + /// Get the native socket implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.at_mark(impl, ec); + } + + /// Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.available(impl, ec); + } + + // Bind the raw socket to the specified local endpoint. + ASIO_SYNC_OP_VOID bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + service_impl_.bind(impl, endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the raw socket to the specified endpoint. + ASIO_SYNC_OP_VOID connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + service_impl_.connect(impl, peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + template + ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void (asio::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(ConnectHandler) handler) + { + async_completion init(handler); + + service_impl_.async_connect(impl, peer_endpoint, init.completion_handler); + + return init.result.get(); + } + + /// Set a socket option. + template + ASIO_SYNC_OP_VOID set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + service_impl_.set_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get a socket option. + template + ASIO_SYNC_OP_VOID get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + service_impl_.get_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + template + ASIO_SYNC_OP_VOID io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + service_impl_.io_control(impl, command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + bool non_blocking(const implementation_type& impl) const + { + return service_impl_.non_blocking(impl); + } + + /// Sets the non-blocking mode of the socket. + ASIO_SYNC_OP_VOID non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return service_impl_.native_non_blocking(impl); + } + + /// Sets the non-blocking mode of the native socket implementation. + ASIO_SYNC_OP_VOID native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.native_non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.remote_endpoint(impl, ec); + } + + /// Disable sends or receives on the socket. + ASIO_SYNC_OP_VOID shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + service_impl_.shutdown(impl, what, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + ASIO_SYNC_OP_VOID wait(implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + service_impl_.wait(impl, w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, socket_base::wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, w, init.completion_handler); + + return init.result.get(); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send(impl, buffers, flags, ec); + } + + /// Start an asynchronous send. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + async_completion init(handler); + + service_impl_.async_send(impl, buffers, flags, init.completion_handler); + + return init.result.get(); + } + + /// Send raw data to the specified endpoint. + template + std::size_t send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send_to(impl, buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + async_completion init(handler); + + service_impl_.async_send_to(impl, buffers, + destination, flags, init.completion_handler); + + return init.result.get(); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive(impl, buffers, flags, ec); + } + + /// Start an asynchronous receive. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + async_completion init(handler); + + service_impl_.async_receive(impl, buffers, flags, init.completion_handler); + + return init.result.get(); + } + + /// Receive raw data with the endpoint of the sender. + template + std::size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, + ec); + } + + /// Start an asynchronous receive that will get the endpoint of the sender. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + async_completion init(handler); + + service_impl_.async_receive_from(impl, buffers, + sender_endpoint, flags, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_RAW_SOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/read.hpp b/subprojects/asio/include/asio/read.hpp new file mode 100644 index 000000000..02e8b6778 --- /dev/null +++ b/subprojects/asio/include/asio/read.hpp @@ -0,0 +1,947 @@ +// +// read.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_READ_HPP +#define ASIO_READ_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/buffer.hpp" +#include "asio/error.hpp" + +#if !defined(ASIO_NO_EXTENSIONS) +# include "asio/basic_streambuf_fwd.hpp" +#endif // !defined(ASIO_NO_EXTENSIONS) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/** + * @defgroup read asio::read + * + * @brief The @c read function is a composed operation that reads a certain + * amount of data from a stream before returning. + */ +/*@{*/ + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read(s, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + typename enable_if< + is_mutable_buffer_sequence::value + >::type* = 0); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read(s, asio::buffer(data, size), ec); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, buffers, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + asio::error_code& ec, + typename enable_if< + is_mutable_buffer_sequence::value + >::type* = 0); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's read_some function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read(s, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, + typename enable_if< + is_mutable_buffer_sequence::value + >::type* = 0); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's read_some function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_mutable_buffer_sequence::value + >::type* = 0); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The specified dynamic buffer sequence is full (that is, it has reached + * maximum size). + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffer is full (that is, it has reached maximum size). + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, buffers, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The specified dynamic buffer sequence is full (that is, it has reached + * maximum size). + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's read_some function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The specified dynamic buffer sequence is full (that is, it has reached + * maximum size). + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's read_some function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffer is full (that is, it has reached maximum size). + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t read(SyncReadStream& s, basic_streambuf& b); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffer is full (that is, it has reached maximum size). + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, b, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t read(SyncReadStream& s, basic_streambuf& b, + asio::error_code& ec); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffer is full (that is, it has reached maximum size). + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's read_some function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t read(SyncReadStream& s, basic_streambuf& b, + CompletionCondition completion_condition); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffer is full (that is, it has reached maximum size). + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's read_some function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read(SyncReadStream& s, basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ +/** + * @defgroup async_read asio::async_read + * + * @brief The @c async_read function is a composed asynchronous operation that + * reads a certain amount of data from a stream before completion. + */ +/*@{*/ + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other read operations (such + * as async_read, the stream's async_read_some function, or any other composed + * operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_read(s, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::async_read( + * s, buffers, + * asio::transfer_all(), + * handler); @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_mutable_buffer_sequence::value + >::type* = 0); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's async_read_some function. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::async_read(s, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_mutable_buffer_sequence::value + >::type* = 0); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The specified dynamic buffer sequence is full (that is, it has reached + * maximum size). + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other read operations (such + * as async_read, the stream's async_read_some function, or any other composed + * operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note This overload is equivalent to calling: + * @code asio::async_read( + * s, buffers, + * asio::transfer_all(), + * handler); @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The specified dynamic buffer sequence is full (that is, it has reached + * maximum size). + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other read operations (such + * as async_read, the stream's async_read_some function, or any other composed + * operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's async_read_some function. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffer is full (that is, it has reached maximum size). + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other read operations (such + * as async_read, the stream's async_read_some function, or any other composed + * operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note This overload is equivalent to calling: + * @code asio::async_read( + * s, b, + * asio::transfer_all(), + * handler); @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, basic_streambuf& b, + ASIO_MOVE_ARG(ReadHandler) handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffer is full (that is, it has reached maximum size). + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other read operations (such + * as async_read, the stream's async_read_some function, or any other composed + * operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_read_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the stream's async_read_some function. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read(AsyncReadStream& s, basic_streambuf& b, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/read.hpp" + +#endif // ASIO_READ_HPP diff --git a/subprojects/asio/include/asio/read_at.hpp b/subprojects/asio/include/asio/read_at.hpp new file mode 100644 index 000000000..3e25a9152 --- /dev/null +++ b/subprojects/asio/include/asio/read_at.hpp @@ -0,0 +1,671 @@ +// +// read_at.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_READ_AT_HPP +#define ASIO_READ_AT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/error.hpp" + +#if !defined(ASIO_NO_EXTENSIONS) +# include "asio/basic_streambuf_fwd.hpp" +#endif // !defined(ASIO_NO_EXTENSIONS) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/** + * @defgroup read_at asio::read_at + * + * @brief The @c read_at function is a composed operation that reads a certain + * amount of data at the specified offset before returning. + */ +/*@{*/ + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read_at(d, 42, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read_at( + * d, 42, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read_at(d, 42, + * asio::buffer(data, size), ec); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read_at( + * d, 42, buffers, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + asio::error_code& ec); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the device's read_some_at function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read_at(d, 42, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the device's read_some_at function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::read_at( + * d, 42, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, basic_streambuf& b); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @note This overload is equivalent to calling: + * @code asio::read_at( + * d, 42, b, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, basic_streambuf& b, + asio::error_code& ec); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the device's read_some_at function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, basic_streambuf& b, + CompletionCondition completion_condition); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the device's read_some_at function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + uint64_t offset, basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ +/** + * @defgroup async_read_at asio::async_read_at + * + * @brief The @c async_read_at function is a composed asynchronous operation + * that reads a certain amount of data at the specified offset. + */ +/*@{*/ + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * async_read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_read_at(d, 42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::async_read_at( + * d, 42, buffers, + * asio::transfer_all(), + * handler); @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, uint64_t offset, + const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler); + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the device's async_read_some_at function. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::async_read_at(d, 42, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, + uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * async_read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note This overload is equivalent to calling: + * @code asio::async_read_at( + * d, 42, b, + * asio::transfer_all(), + * handler); @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, uint64_t offset, + basic_streambuf& b, ASIO_MOVE_ARG(ReadHandler) handler); + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * async_read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the read operation is complete. A non-zero + * return value indicates the maximum number of bytes to be read on the next + * call to the device's async_read_some_at function. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_at(AsyncRandomAccessReadDevice& d, + uint64_t offset, basic_streambuf& b, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(ReadHandler) handler); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/read_at.hpp" + +#endif // ASIO_READ_AT_HPP diff --git a/subprojects/asio/include/asio/read_until.hpp b/subprojects/asio/include/asio/read_until.hpp new file mode 100644 index 000000000..a346d4ba6 --- /dev/null +++ b/subprojects/asio/include/asio/read_until.hpp @@ -0,0 +1,1826 @@ +// +// read_until.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_READ_UNTIL_HPP +#define ASIO_READ_UNTIL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/async_result.hpp" +#include "asio/detail/regex_fwd.hpp" +#include "asio/detail/string_view.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#if !defined(ASIO_NO_EXTENSIONS) +# include "asio/basic_streambuf_fwd.hpp" +#endif // !defined(ASIO_NO_EXTENSIONS) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + char (&has_result_type_helper(...))[2]; + + template + char has_result_type_helper(T*, typename T::result_type* = 0); + + template + struct has_result_type + { + enum { value = (sizeof((has_result_type_helper)((T*)(0))) == 1) }; + }; +} // namespace detail + +/// Type trait used to determine whether a type can be used as a match condition +/// function with read_until and async_read_until. +template +struct is_match_condition +{ +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true if the type may be used as a match condition. + static const bool value; +#else + enum + { + value = asio::is_function< + typename asio::remove_pointer::type>::value + || detail::has_result_type::value + }; +#endif +}; + +/** + * @defgroup read_until asio::read_until + * + * @brief The @c read_until function is a composed operation that reads data + * into a dynamic buffer sequence, or into a streambuf, until it contains a + * delimiter, matches a regular expression, or a function object indicates a + * match. + */ +/*@{*/ + +/// Read data into a dynamic buffer sequence until it contains a specified +/// delimiter. +/** + * This function is used to read data into the specified dynamic buffer + * sequence until the dynamic buffer sequence's get area contains the specified + * delimiter. The call will block until one of the following conditions is + * true: + * + * @li The get area of the dynamic buffer sequence contains the specified + * delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the dynamic buffer sequence's get area already + * contains the delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @param delim The delimiter character. + * + * @returns The number of bytes in the dynamic buffer sequence's get area up to + * and including the delimiter. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond the delimiter. An application will + * typically leave that data in the dynamic buffer sequence for a subsequent + * read_until operation to examine. + * + * @par Example + * To read data into a @c std::string until a newline is encountered: + * @code std::string data; + * std::string n = asio::read_until(s, + * asio::dynamic_buffer(data), '\n'); + * std::string line = data.substr(0, n); + * data.erase(0, n); @endcode + * After the @c read_until operation completes successfully, the string @c data + * contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\n', 'd', 'e', ... } @endcode + * The call to @c substr then extracts the data up to and including the + * delimiter, so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\n' } @endcode + * After the call to @c erase, the remaining data is left in the buffer @c b as + * follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c read_until operation. + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, char delim); + +/// Read data into a dynamic buffer sequence until it contains a specified +/// delimiter. +/** + * This function is used to read data into the specified dynamic buffer + * sequence until the dynamic buffer sequence's get area contains the specified + * delimiter. The call will block until one of the following conditions is + * true: + * + * @li The get area of the dynamic buffer sequence contains the specified + * delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the dynamic buffer sequence's get area already + * contains the delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @param delim The delimiter character. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the dynamic buffer sequence's get area up to + * and including the delimiter. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond the delimiter. An application will + * typically leave that data in the dynamic buffer sequence for a subsequent + * read_until operation to examine. + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + char delim, asio::error_code& ec); + +/// Read data into a dynamic buffer sequence until it contains a specified +/// delimiter. +/** + * This function is used to read data into the specified dynamic buffer + * sequence until the dynamic buffer sequence's get area contains the specified + * delimiter. The call will block until one of the following conditions is + * true: + * + * @li The get area of the dynamic buffer sequence contains the specified + * delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the dynamic buffer sequence's get area already + * contains the delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @param delim The delimiter string. + * + * @returns The number of bytes in the dynamic buffer sequence's get area up to + * and including the delimiter. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond the delimiter. An application will + * typically leave that data in the dynamic buffer sequence for a subsequent + * read_until operation to examine. + * + * @par Example + * To read data into a @c std::string until a CR-LF sequence is encountered: + * @code std::string data; + * std::string n = asio::read_until(s, + * asio::dynamic_buffer(data), "\r\n"); + * std::string line = data.substr(0, n); + * data.erase(0, n); @endcode + * After the @c read_until operation completes successfully, the string @c data + * contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c substr then extracts the data up to and including the + * delimiter, so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode + * After the call to @c erase, the remaining data is left in the buffer @c b as + * follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c read_until operation. + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_STRING_VIEW_PARAM delim); + +/// Read data into a dynamic buffer sequence until it contains a specified +/// delimiter. +/** + * This function is used to read data into the specified dynamic buffer + * sequence until the dynamic buffer sequence's get area contains the specified + * delimiter. The call will block until one of the following conditions is + * true: + * + * @li The get area of the dynamic buffer sequence contains the specified + * delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the dynamic buffer sequence's get area already + * contains the delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * + * @param delim The delimiter string. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the dynamic buffer sequence's get area up to + * and including the delimiter. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond the delimiter. An application will + * typically leave that data in the dynamic buffer sequence for a subsequent + * read_until operation to examine. + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_STRING_VIEW_PARAM delim, + asio::error_code& ec); + +#if !defined(ASIO_NO_EXTENSIONS) +#if defined(ASIO_HAS_BOOST_REGEX) \ + || defined(GENERATING_DOCUMENTATION) + +/// Read data into a dynamic buffer sequence until some part of the data it +/// contains matches a regular expression. +/** + * This function is used to read data into the specified dynamic buffer + * sequence until the dynamic buffer sequence's get area contains some data + * that matches a regular expression. The call will block until one of the + * following conditions is true: + * + * @li A substring of the dynamic buffer sequence's get area matches the + * regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the dynamic buffer sequence's get area already + * contains data that matches the regular expression, the function returns + * immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers A dynamic buffer sequence into which the data will be read. + * + * @param expr The regular expression. + * + * @returns The number of bytes in the dynamic buffer sequence's get area up to + * and including the substring that matches the regular expression. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond that which matched the regular + * expression. An application will typically leave that data in the dynamic + * buffer sequence for a subsequent read_until operation to examine. + * + * @par Example + * To read data into a @c std::string until a CR-LF sequence is encountered: + * @code std::string data; + * std::string n = asio::read_until(s, + * asio::dynamic_buffer(data), boost::regex("\r\n")); + * std::string line = data.substr(0, n); + * data.erase(0, n); @endcode + * After the @c read_until operation completes successfully, the string @c data + * contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c substr then extracts the data up to and including the + * delimiter, so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode + * After the call to @c erase, the remaining data is left in the buffer @c b as + * follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c read_until operation. + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + const boost::regex& expr); + +/// Read data into a dynamic buffer sequence until some part of the data it +/// contains matches a regular expression. +/** + * This function is used to read data into the specified dynamic buffer + * sequence until the dynamic buffer sequence's get area contains some data + * that matches a regular expression. The call will block until one of the + * following conditions is true: + * + * @li A substring of the dynamic buffer sequence's get area matches the + * regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the dynamic buffer sequence's get area already + * contains data that matches the regular expression, the function returns + * immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers A dynamic buffer sequence into which the data will be read. + * + * @param expr The regular expression. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the dynamic buffer sequence's get area up to + * and including the substring that matches the regular expression. Returns 0 + * if an error occurred. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond that which matched the regular + * expression. An application will typically leave that data in the dynamic + * buffer sequence for a subsequent read_until operation to examine. + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + const boost::regex& expr, asio::error_code& ec); + +#endif // defined(ASIO_HAS_BOOST_REGEX) + // || defined(GENERATING_DOCUMENTATION) + +/// Read data into a dynamic buffer sequence until a function object indicates a +/// match. + +/** + * This function is used to read data into the specified dynamic buffer + * sequence until a user-defined match condition function object, when applied + * to the data contained in the dynamic buffer sequence, indicates a successful + * match. The call will block until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the match condition function object already indicates + * a match, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers A dynamic buffer sequence into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @returns The number of bytes in the dynamic_buffer's get area that + * have been fully consumed by the match function. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond that which matched the function object. + * An application will typically leave that data in the dynamic buffer sequence + * for a subsequent read_until operation to examine. + + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + * + * @par Examples + * To read data into a dynamic buffer sequence until whitespace is encountered: + * @code typedef asio::buffers_iterator< + * asio::const_buffers_1> iterator; + * + * std::pair + * match_whitespace(iterator begin, iterator end) + * { + * iterator i = begin; + * while (i != end) + * if (std::isspace(*i++)) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * ... + * std::string data; + * asio::read_until(s, data, match_whitespace); + * @endcode + * + * To read data into a @c std::string until a matching character is found: + * @code class match_char + * { + * public: + * explicit match_char(char c) : c_(c) {} + * + * template + * std::pair operator()( + * Iterator begin, Iterator end) const + * { + * Iterator i = begin; + * while (i != end) + * if (c_ == *i++) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * + * private: + * char c_; + * }; + * + * namespace asio { + * template <> struct is_match_condition + * : public boost::true_type {}; + * } // namespace asio + * ... + * std::string data; + * asio::read_until(s, data, match_char('a')); + * @endcode + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + MatchCondition match_condition, + typename enable_if::value>::type* = 0); + +/// Read data into a dynamic buffer sequence until a function object indicates a +/// match. +/** + * This function is used to read data into the specified dynamic buffer + * sequence until a user-defined match condition function object, when applied + * to the data contained in the dynamic buffer sequence, indicates a successful + * match. The call will block until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the match condition function object already indicates + * a match, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers A dynamic buffer sequence into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the dynamic buffer sequence's get area that + * have been fully consumed by the match function. Returns 0 if an error + * occurred. + * + * @note After a successful read_until operation, the dynamic buffer sequence + * may contain additional data beyond that which matched the function object. + * An application will typically leave that data in the dynamic buffer sequence + * for a subsequent read_until operation to examine. + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + */ +template +std::size_t read_until(SyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + MatchCondition match_condition, asio::error_code& ec, + typename enable_if::value>::type* = 0); + +#if !defined(ASIO_NO_IOSTREAM) + +/// Read data into a streambuf until it contains a specified delimiter. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter character. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. + * + * @par Example + * To read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, '\n'); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + * After the @c read_until operation completes successfully, the buffer @c b + * contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\n', 'd', 'e', ... } @endcode + * The call to @c std::getline then extracts the data up to and including the + * newline (which is discarded), so that the string @c line contains: + * @code { 'a', 'b', ..., 'c' } @endcode + * The remaining data is left in the buffer @c b as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c read_until operation. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim); + +/// Read data into a streambuf until it contains a specified delimiter. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter character. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim, + asio::error_code& ec); + +/// Read data into a streambuf until it contains a specified delimiter. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter string. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. + * + * @par Example + * To read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, "\r\n"); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + * After the @c read_until operation completes successfully, the buffer @c b + * contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c std::getline then extracts the data up to and including the + * newline (which is discarded), so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r' } @endcode + * The remaining data is left in the buffer @c b as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c read_until operation. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + ASIO_STRING_VIEW_PARAM delim); + +/// Read data into a streambuf until it contains a specified delimiter. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter string. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + ASIO_STRING_VIEW_PARAM delim, asio::error_code& ec); + +#if defined(ASIO_HAS_BOOST_REGEX) \ + || defined(GENERATING_DOCUMENTATION) + +/// Read data into a streambuf until some part of the data it contains matches +/// a regular expression. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains some data that matches a regular expression. + * The call will block until one of the following conditions is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains data that + * matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param expr The regular expression. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the substring that matches the regular expression. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the regular expression. An + * application will typically leave that data in the streambuf for a subsequent + * read_until operation to examine. + * + * @par Example + * To read data into a streambuf until a CR-LF sequence is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, boost::regex("\r\n")); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + * After the @c read_until operation completes successfully, the buffer @c b + * contains the data which matched the regular expression: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c std::getline then extracts the data up to and including the + * newline (which is discarded), so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r' } @endcode + * The remaining data is left in the buffer @c b as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c read_until operation. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr); + +/// Read data into a streambuf until some part of the data it contains matches +/// a regular expression. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains some data that matches a regular expression. + * The call will block until one of the following conditions is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains data that + * matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param expr The regular expression. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the substring that matches the regular expression. Returns 0 if an error + * occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the regular expression. An + * application will typically leave that data in the streambuf for a subsequent + * read_until operation to examine. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + asio::error_code& ec); + +#endif // defined(ASIO_HAS_BOOST_REGEX) + // || defined(GENERATING_DOCUMENTATION) + +/// Read data into a streambuf until a function object indicates a match. +/** + * This function is used to read data into the specified streambuf until a + * user-defined match condition function object, when applied to the data + * contained in the streambuf, indicates a successful match. The call will + * block until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the match condition function object already indicates + * a match, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator::const_buffers_type> + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @returns The number of bytes in the streambuf's get area that have been fully + * consumed by the match function. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the function object. An application + * will typically leave that data in the streambuf for a subsequent read_until + * operation to examine. + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + * + * @par Examples + * To read data into a streambuf until whitespace is encountered: + * @code typedef asio::buffers_iterator< + * asio::streambuf::const_buffers_type> iterator; + * + * std::pair + * match_whitespace(iterator begin, iterator end) + * { + * iterator i = begin; + * while (i != end) + * if (std::isspace(*i++)) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * ... + * asio::streambuf b; + * asio::read_until(s, b, match_whitespace); + * @endcode + * + * To read data into a streambuf until a matching character is found: + * @code class match_char + * { + * public: + * explicit match_char(char c) : c_(c) {} + * + * template + * std::pair operator()( + * Iterator begin, Iterator end) const + * { + * Iterator i = begin; + * while (i != end) + * if (c_ == *i++) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * + * private: + * char c_; + * }; + * + * namespace asio { + * template <> struct is_match_condition + * : public boost::true_type {}; + * } // namespace asio + * ... + * asio::streambuf b; + * asio::read_until(s, b, match_char('a')); + * @endcode + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, MatchCondition match_condition, + typename enable_if::value>::type* = 0); + +/// Read data into a streambuf until a function object indicates a match. +/** + * This function is used to read data into the specified streambuf until a + * user-defined match condition function object, when applied to the data + * contained in the streambuf, indicates a successful match. The call will + * block until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the match condition function object already indicates + * a match, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator::const_buffers_type> + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area that have been fully + * consumed by the match function. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the function object. An application + * will typically leave that data in the streambuf for a subsequent read_until + * operation to examine. + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, asio::error_code& ec, + typename enable_if::value>::type* = 0); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ +/** + * @defgroup async_read_until asio::async_read_until + * + * @brief The @c async_read_until function is a composed asynchronous operation + * that reads data into a dynamic buffer sequence, or into a streambuf, until + * it contains a delimiter, matches a regular expression, or a function object + * indicates a match. + */ +/*@{*/ + +/// Start an asynchronous operation to read data into a dynamic buffer sequence +/// until it contains a specified delimiter. +/** + * This function is used to asynchronously read data into the specified dynamic + * buffer sequence until the dynamic buffer sequence's get area contains the + * specified delimiter. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li The get area of the dynamic buffer sequence contains the specified + * delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the dynamic buffer sequence's get area already contains the delimiter, this + * asynchronous operation completes immediately. The program must ensure that + * the stream performs no other read operations (such as async_read, + * async_read_until, the stream's async_read_some function, or any other + * composed operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param delim The delimiter character. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the dynamic buffer sequence's + * // get area up to and including the delimiter. + * // 0 if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the dynamic buffer + * sequence may contain additional data beyond the delimiter. An application + * will typically leave that data in the dynamic buffer sequence for a + * subsequent async_read_until operation to examine. + * + * @par Example + * To asynchronously read data into a @c std::string until a newline is + * encountered: + * @code std::string data; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::string line = data.substr(0, n); + * data.erase(0, n); + * ... + * } + * } + * ... + * asio::async_read_until(s, data, '\n', handler); @endcode + * After the @c async_read_until operation completes successfully, the buffer + * @c data contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\n', 'd', 'e', ... } @endcode + * The call to @c substr then extracts the data up to and including the + * delimiter, so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\n' } @endcode + * After the call to @c erase, the remaining data is left in the buffer @c data + * as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c async_read_until operation. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + char delim, ASIO_MOVE_ARG(ReadHandler) handler); + +/// Start an asynchronous operation to read data into a dynamic buffer sequence +/// until it contains a specified delimiter. +/** + * This function is used to asynchronously read data into the specified dynamic + * buffer sequence until the dynamic buffer sequence's get area contains the + * specified delimiter. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li The get area of the dynamic buffer sequence contains the specified + * delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the dynamic buffer sequence's get area already contains the delimiter, this + * asynchronous operation completes immediately. The program must ensure that + * the stream performs no other read operations (such as async_read, + * async_read_until, the stream's async_read_some function, or any other + * composed operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param delim The delimiter string. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the dynamic buffer sequence's + * // get area up to and including the delimiter. + * // 0 if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the dynamic buffer + * sequence may contain additional data beyond the delimiter. An application + * will typically leave that data in the dynamic buffer sequence for a + * subsequent async_read_until operation to examine. + * + * @par Example + * To asynchronously read data into a @c std::string until a CR-LF sequence is + * encountered: + * @code std::string data; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::string line = data.substr(0, n); + * data.erase(0, n); + * ... + * } + * } + * ... + * asio::async_read_until(s, data, "\r\n", handler); @endcode + * After the @c async_read_until operation completes successfully, the string + * @c data contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c substr then extracts the data up to and including the + * delimiter, so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode + * After the call to @c erase, the remaining data is left in the string @c data + * as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c async_read_until operation. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_STRING_VIEW_PARAM delim, + ASIO_MOVE_ARG(ReadHandler) handler); + +#if !defined(ASIO_NO_EXTENSIONS) +#if defined(ASIO_HAS_BOOST_REGEX) \ + || defined(GENERATING_DOCUMENTATION) + +/// Start an asynchronous operation to read data into a dynamic buffer sequence +/// until some part of its data matches a regular expression. +/** + * This function is used to asynchronously read data into the specified dynamic + * buffer sequence until the dynamic buffer sequence's get area contains some + * data that matches a regular expression. The function call always returns + * immediately. The asynchronous operation will continue until one of the + * following conditions is true: + * + * @li A substring of the dynamic buffer sequence's get area matches the regular + * expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the dynamic buffer sequence's get area already contains data that matches + * the regular expression, this asynchronous operation completes immediately. + * The program must ensure that the stream performs no other read operations + * (such as async_read, async_read_until, the stream's async_read_some + * function, or any other composed operations that perform reads) until this + * operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param expr The regular expression. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the dynamic buffer + * // sequence's get area up to and including the + * // substring that matches the regular expression. + * // 0 if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the dynamic buffer + * sequence may contain additional data beyond that which matched the regular + * expression. An application will typically leave that data in the dynamic + * buffer sequence for a subsequent async_read_until operation to examine. + * + * @par Example + * To asynchronously read data into a @c std::string until a CR-LF sequence is + * encountered: + * @code std::string data; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::string line = data.substr(0, n); + * data.erase(0, n); + * ... + * } + * } + * ... + * asio::async_read_until(s, data, + * boost::regex("\r\n"), handler); @endcode + * After the @c async_read_until operation completes successfully, the string + * @c data contains the data which matched the regular expression: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c substr then extracts the data up to and including the match, + * so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode + * After the call to @c erase, the remaining data is left in the string @c data + * as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c async_read_until operation. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + const boost::regex& expr, + ASIO_MOVE_ARG(ReadHandler) handler); + +#endif // defined(ASIO_HAS_BOOST_REGEX) + // || defined(GENERATING_DOCUMENTATION) + +/// Start an asynchronous operation to read data into a dynamic buffer sequence +/// until a function object indicates a match. +/** + * This function is used to asynchronously read data into the specified dynamic + * buffer sequence until a user-defined match condition function object, when + * applied to the data contained in the dynamic buffer sequence, indicates a + * successful match. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the match condition function object already indicates a match, this + * asynchronous operation completes immediately. The program must ensure that + * the stream performs no other read operations (such as async_read, + * async_read_until, the stream's async_read_some function, or any other + * composed operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers The dynamic buffer sequence into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the dynamic buffer sequence's + * // get area that have been fully consumed by the match + * // function. O if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the dynamic buffer + * sequence may contain additional data beyond that which matched the function + * object. An application will typically leave that data in the dynamic buffer + * sequence for a subsequent async_read_until operation to examine. + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + * + * @par Examples + * To asynchronously read data into a @c std::string until whitespace is + * encountered: + * @code typedef asio::buffers_iterator< + * asio::const_buffers_1> iterator; + * + * std::pair + * match_whitespace(iterator begin, iterator end) + * { + * iterator i = begin; + * while (i != end) + * if (std::isspace(*i++)) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * ... + * void handler(const asio::error_code& e, std::size_t size); + * ... + * std::string data; + * asio::async_read_until(s, data, match_whitespace, handler); + * @endcode + * + * To asynchronously read data into a @c std::string until a matching character + * is found: + * @code class match_char + * { + * public: + * explicit match_char(char c) : c_(c) {} + * + * template + * std::pair operator()( + * Iterator begin, Iterator end) const + * { + * Iterator i = begin; + * while (i != end) + * if (c_ == *i++) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * + * private: + * char c_; + * }; + * + * namespace asio { + * template <> struct is_match_condition + * : public boost::true_type {}; + * } // namespace asio + * ... + * void handler(const asio::error_code& e, std::size_t size); + * ... + * std::string data; + * asio::async_read_until(s, data, match_char('a'), handler); + * @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + MatchCondition match_condition, ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if::value>::type* = 0); + +#if !defined(ASIO_NO_IOSTREAM) + +/// Start an asynchronous operation to read data into a streambuf until it +/// contains a specified delimiter. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains the specified delimiter. + * The function call always returns immediately. The asynchronous operation + * will continue until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the streambuf's get area already contains the delimiter, this asynchronous + * operation completes immediately. The program must ensure that the stream + * performs no other read operations (such as async_read, async_read_until, the + * stream's async_read_some function, or any other composed operations that + * perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param delim The delimiter character. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the streambuf's get + * // area up to and including the delimiter. + * // 0 if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond the delimiter. An application will typically + * leave that data in the streambuf for a subsequent async_read_until operation + * to examine. + * + * @par Example + * To asynchronously read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, '\n', handler); @endcode + * After the @c async_read_until operation completes successfully, the buffer + * @c b contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\n', 'd', 'e', ... } @endcode + * The call to @c std::getline then extracts the data up to and including the + * newline (which is discarded), so that the string @c line contains: + * @code { 'a', 'b', ..., 'c' } @endcode + * The remaining data is left in the buffer @c b as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c async_read_until operation. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + char delim, ASIO_MOVE_ARG(ReadHandler) handler); + +/// Start an asynchronous operation to read data into a streambuf until it +/// contains a specified delimiter. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains the specified delimiter. + * The function call always returns immediately. The asynchronous operation + * will continue until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the streambuf's get area already contains the delimiter, this asynchronous + * operation completes immediately. The program must ensure that the stream + * performs no other read operations (such as async_read, async_read_until, the + * stream's async_read_some function, or any other composed operations that + * perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param delim The delimiter string. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the streambuf's get + * // area up to and including the delimiter. + * // 0 if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond the delimiter. An application will typically + * leave that data in the streambuf for a subsequent async_read_until operation + * to examine. + * + * @par Example + * To asynchronously read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, "\r\n", handler); @endcode + * After the @c async_read_until operation completes successfully, the buffer + * @c b contains the delimiter: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c std::getline then extracts the data up to and including the + * newline (which is discarded), so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r' } @endcode + * The remaining data is left in the buffer @c b as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c async_read_until operation. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + ASIO_STRING_VIEW_PARAM delim, + ASIO_MOVE_ARG(ReadHandler) handler); + +#if defined(ASIO_HAS_BOOST_REGEX) \ + || defined(GENERATING_DOCUMENTATION) + +/// Start an asynchronous operation to read data into a streambuf until some +/// part of its data matches a regular expression. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains some data that matches a + * regular expression. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the streambuf's get area already contains data that matches the regular + * expression, this asynchronous operation completes immediately. The program + * must ensure that the stream performs no other read operations (such as + * async_read, async_read_until, the stream's async_read_some function, or any + * other composed operations that perform reads) until this operation + * completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param expr The regular expression. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the streambuf's get + * // area up to and including the substring + * // that matches the regular. expression. + * // 0 if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond that which matched the regular expression. An + * application will typically leave that data in the streambuf for a subsequent + * async_read_until operation to examine. + * + * @par Example + * To asynchronously read data into a streambuf until a CR-LF sequence is + * encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, boost::regex("\r\n"), handler); @endcode + * After the @c async_read_until operation completes successfully, the buffer + * @c b contains the data which matched the regular expression: + * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode + * The call to @c std::getline then extracts the data up to and including the + * newline (which is discarded), so that the string @c line contains: + * @code { 'a', 'b', ..., 'c', '\r' } @endcode + * The remaining data is left in the buffer @c b as follows: + * @code { 'd', 'e', ... } @endcode + * This data may be the start of a new line, to be extracted by a subsequent + * @c async_read_until operation. + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + ASIO_MOVE_ARG(ReadHandler) handler); + +#endif // defined(ASIO_HAS_BOOST_REGEX) + // || defined(GENERATING_DOCUMENTATION) + +/// Start an asynchronous operation to read data into a streambuf until a +/// function object indicates a match. +/** + * This function is used to asynchronously read data into the specified + * streambuf until a user-defined match condition function object, when applied + * to the data contained in the streambuf, indicates a successful match. The + * function call always returns immediately. The asynchronous operation will + * continue until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function, and is known as a composed operation. If + * the match condition function object already indicates a match, this + * asynchronous operation completes immediately. The program must ensure that + * the stream performs no other read operations (such as async_read, + * async_read_until, the stream's async_read_some function, or any other + * composed operations that perform reads) until this operation completes. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator::const_buffers_type> + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the streambuf's get + * // area that have been fully consumed by the + * // match function. O if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond that which matched the function object. An + * application will typically leave that data in the streambuf for a subsequent + * async_read_until operation to examine. + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + * + * @par Examples + * To asynchronously read data into a streambuf until whitespace is encountered: + * @code typedef asio::buffers_iterator< + * asio::streambuf::const_buffers_type> iterator; + * + * std::pair + * match_whitespace(iterator begin, iterator end) + * { + * iterator i = begin; + * while (i != end) + * if (std::isspace(*i++)) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * ... + * void handler(const asio::error_code& e, std::size_t size); + * ... + * asio::streambuf b; + * asio::async_read_until(s, b, match_whitespace, handler); + * @endcode + * + * To asynchronously read data into a streambuf until a matching character is + * found: + * @code class match_char + * { + * public: + * explicit match_char(char c) : c_(c) {} + * + * template + * std::pair operator()( + * Iterator begin, Iterator end) const + * { + * Iterator i = begin; + * while (i != end) + * if (c_ == *i++) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * + * private: + * char c_; + * }; + * + * namespace asio { + * template <> struct is_match_condition + * : public boost::true_type {}; + * } // namespace asio + * ... + * void handler(const asio::error_code& e, std::size_t size); + * ... + * asio::streambuf b; + * asio::async_read_until(s, b, match_char('a'), handler); + * @endcode + */ +template +ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) +async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, ASIO_MOVE_ARG(ReadHandler) handler, + typename enable_if::value>::type* = 0); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/read_until.hpp" + +#endif // ASIO_READ_UNTIL_HPP diff --git a/subprojects/asio/include/asio/seq_packet_socket_service.hpp b/subprojects/asio/include/asio/seq_packet_socket_service.hpp new file mode 100644 index 000000000..7e6824d41 --- /dev/null +++ b/subprojects/asio/include/asio/seq_packet_socket_service.hpp @@ -0,0 +1,416 @@ +// +// seq_packet_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SEQ_PACKET_SOCKET_SERVICE_HPP +#define ASIO_SEQ_PACKET_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a sequenced packet socket. +template +class seq_packet_socket_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base< + seq_packet_socket_service > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_WINDOWS_RUNTIME) + typedef detail::null_socket_service service_impl_type; +#elif defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#else + typedef detail::reactive_socket_service service_impl_type; +#endif + +public: + /// The type of a sequenced packet socket implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new sequenced packet socket service for the specified + /// io_context. + explicit seq_packet_socket_service(asio::io_context& io_context) + : asio::detail::service_base< + seq_packet_socket_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new sequenced packet socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new sequenced packet socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another sequenced packet socket implementation. + void move_assign(implementation_type& impl, + seq_packet_socket_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } + + // All socket services have access to each other's implementations. + template friend class seq_packet_socket_service; + + /// Move-construct a new sequenced packet socket implementation from another + /// protocol type. + template + void converting_move_construct(implementation_type& impl, + seq_packet_socket_service& other_service, + typename seq_packet_socket_service< + Protocol1>::implementation_type& other_impl, + typename enable_if::value>::type* = 0) + { + service_impl_.template converting_move_construct( + impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a sequenced packet socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a sequenced packet socket. + ASIO_SYNC_OP_VOID open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (protocol.type() == ASIO_OS_DEF(SOCK_SEQPACKET)) + service_impl_.open(impl, protocol, ec); + else + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to a sequenced packet socket. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + service_impl_.assign(impl, protocol, native_socket, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a sequenced packet socket implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying socket. + native_handle_type release(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.release(impl, ec); + } + + /// Get the native socket implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.at_mark(impl, ec); + } + + /// Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.available(impl, ec); + } + + /// Bind the sequenced packet socket to the specified local endpoint. + ASIO_SYNC_OP_VOID bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + service_impl_.bind(impl, endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the sequenced packet socket to the specified endpoint. + ASIO_SYNC_OP_VOID connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + service_impl_.connect(impl, peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + template + ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void (asio::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(ConnectHandler) handler) + { + async_completion init(handler); + + service_impl_.async_connect(impl, peer_endpoint, init.completion_handler); + + return init.result.get(); + } + + /// Set a socket option. + template + ASIO_SYNC_OP_VOID set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + service_impl_.set_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get a socket option. + template + ASIO_SYNC_OP_VOID get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + service_impl_.get_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + template + ASIO_SYNC_OP_VOID io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + service_impl_.io_control(impl, command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + bool non_blocking(const implementation_type& impl) const + { + return service_impl_.non_blocking(impl); + } + + /// Sets the non-blocking mode of the socket. + ASIO_SYNC_OP_VOID non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return service_impl_.native_non_blocking(impl); + } + + /// Sets the non-blocking mode of the native socket implementation. + ASIO_SYNC_OP_VOID native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.native_non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.remote_endpoint(impl, ec); + } + + /// Disable sends or receives on the socket. + ASIO_SYNC_OP_VOID shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + service_impl_.shutdown(impl, what, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + ASIO_SYNC_OP_VOID wait(implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + service_impl_.wait(impl, w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, socket_base::wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, w, init.completion_handler); + + return init.result.get(); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send(impl, buffers, flags, ec); + } + + /// Start an asynchronous send. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + async_completion init(handler); + + service_impl_.async_send(impl, buffers, flags, init.completion_handler); + + return init.result.get(); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + return service_impl_.receive_with_flags(impl, + buffers, in_flags, out_flags, ec); + } + + /// Start an asynchronous receive. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + async_completion init(handler); + + service_impl_.async_receive_with_flags(impl, + buffers, in_flags, out_flags, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_SEQ_PACKET_SOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/serial_port.hpp b/subprojects/asio/include/asio/serial_port.hpp new file mode 100644 index 000000000..27bb63ef7 --- /dev/null +++ b/subprojects/asio/include/asio/serial_port.hpp @@ -0,0 +1,769 @@ +// +// serial_port.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SERIAL_PORT_HPP +#define ASIO_SERIAL_PORT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/async_result.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/serial_port_base.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/basic_serial_port.hpp" +#else // defined(ASIO_ENABLE_OLD_SERVICES) +# if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_serial_port_service.hpp" +# define ASIO_SVC_T detail::win_iocp_serial_port_service +# else +# include "asio/detail/reactive_serial_port_service.hpp" +# define ASIO_SVC_T detail::reactive_serial_port_service +# endif +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_ENABLE_OLD_SERVICES) +// Typedef for the typical usage of a serial port. +typedef basic_serial_port<> serial_port; +#else // defined(ASIO_ENABLE_OLD_SERVICES) +/// Provides serial port functionality. +/** + * The serial_port class provides a wrapper over serial port functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class serial_port + : ASIO_SVC_ACCESS basic_io_object, + public serial_port_base +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The native representation of a serial port. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef ASIO_SVC_T::native_handle_type native_handle_type; +#endif + + /// A basic_serial_port is always the lowest layer. + typedef serial_port lowest_layer_type; + + /// Construct a serial_port without opening it. + /** + * This constructor creates a serial port without opening it. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + */ + explicit serial_port(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct and open a serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param device The platform-specific device name for this serial + * port. + */ + explicit serial_port(asio::io_context& io_context, + const char* device) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param device The platform-specific device name for this serial + * port. + */ + explicit serial_port(asio::io_context& io_context, + const std::string& device) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct a serial_port on an existing native serial port. + /** + * This constructor creates a serial port object to hold an existing native + * serial port. + * + * @param io_context The io_context object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + serial_port(asio::io_context& io_context, + const native_handle_type& native_serial_port) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a serial_port from another. + /** + * This constructor moves a serial port from one object to another. + * + * @param other The other serial_port object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c serial_port(io_context&) constructor. + */ + serial_port(serial_port&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign a serial_port from another. + /** + * This assignment operator moves a serial port from one object to another. + * + * @param other The other serial_port object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c serial_port(io_context&) constructor. + */ + serial_port& operator=(serial_port&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the serial port. + /** + * This function destroys the serial port, cancelling any outstanding + * asynchronous wait operations associated with the serial port as if by + * calling @c cancel. + */ + ~serial_port() + { + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a serial_port cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a serial_port cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port for the specified device name. + * + * @param device The platform-specific device name. + * + * @throws asio::system_error Thrown on failure. + */ + void open(const std::string& device) + { + asio::error_code ec; + this->get_service().open(this->get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port using the given platform-specific + * device name. + * + * @param device The platform-specific device name. + * + * @param ec Set the indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID open(const std::string& device, + asio::error_code& ec) + { + this->get_service().open(this->get_implementation(), device, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& native_serial_port) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& native_serial_port, + asio::error_code& ec) + { + this->get_service().assign(this->get_implementation(), + native_serial_port, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the serial port is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native serial port representation. + /** + * This function may be used to obtain the underlying representation of the + * serial port. This is intended to allow access to native serial port + * functionality that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void send_break() + { + asio::error_code ec; + this->get_service().send_break(this->get_implementation(), ec); + asio::detail::throw_error(ec, "send_break"); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID send_break(asio::error_code& ec) + { + this->get_service().send_break(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void set_option(const SettableSerialPortOption& option) + { + asio::error_code ec; + this->get_service().set_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + ASIO_SYNC_OP_VOID set_option(const SettableSerialPortOption& option, + asio::error_code& ec) + { + this->get_service().set_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void get_option(GettableSerialPortOption& option) + { + asio::error_code ec; + this->get_service().get_option(this->get_implementation(), option, ec); + asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + ASIO_SYNC_OP_VOID get_option(GettableSerialPortOption& option, + asio::error_code& ec) + { + this->get_service().get_option(this->get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().write_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the serial port. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the serial port. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + async_completion init(handler); + + this->get_service().async_write_some( + this->get_implementation(), buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().read_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the serial port. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + async_completion init(handler); + + this->get_service().async_read_some( + this->get_implementation(), buffers, init.completion_handler); + + return init.result.get(); + } +}; +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) +# undef ASIO_SVC_T +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_SERIAL_PORT_HPP diff --git a/subprojects/asio/include/asio/serial_port_base.hpp b/subprojects/asio/include/asio/serial_port_base.hpp new file mode 100644 index 000000000..feb5b9df8 --- /dev/null +++ b/subprojects/asio/include/asio/serial_port_base.hpp @@ -0,0 +1,167 @@ +// +// serial_port_base.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SERIAL_PORT_BASE_HPP +#define ASIO_SERIAL_PORT_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) +# include +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/socket_types.hpp" +#include "asio/error_code.hpp" + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_OPTION_STORAGE implementation_defined +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# define ASIO_OPTION_STORAGE DCB +#else +# define ASIO_OPTION_STORAGE termios +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// The serial_port_base class is used as a base for the basic_serial_port class +/// template so that we have a common place to define the serial port options. +class serial_port_base +{ +public: + /// Serial port option to permit changing the baud rate. + /** + * Implements changing the baud rate for a given serial port. + */ + class baud_rate + { + public: + explicit baud_rate(unsigned int rate = 0); + unsigned int value() const; + ASIO_DECL ASIO_SYNC_OP_VOID store( + ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + ASIO_DECL ASIO_SYNC_OP_VOID load( + const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + unsigned int value_; + }; + + /// Serial port option to permit changing the flow control. + /** + * Implements changing the flow control for a given serial port. + */ + class flow_control + { + public: + enum type { none, software, hardware }; + ASIO_DECL explicit flow_control(type t = none); + type value() const; + ASIO_DECL ASIO_SYNC_OP_VOID store( + ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + ASIO_DECL ASIO_SYNC_OP_VOID load( + const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + type value_; + }; + + /// Serial port option to permit changing the parity. + /** + * Implements changing the parity for a given serial port. + */ + class parity + { + public: + enum type { none, odd, even }; + ASIO_DECL explicit parity(type t = none); + type value() const; + ASIO_DECL ASIO_SYNC_OP_VOID store( + ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + ASIO_DECL ASIO_SYNC_OP_VOID load( + const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + type value_; + }; + + /// Serial port option to permit changing the number of stop bits. + /** + * Implements changing the number of stop bits for a given serial port. + */ + class stop_bits + { + public: + enum type { one, onepointfive, two }; + ASIO_DECL explicit stop_bits(type t = one); + type value() const; + ASIO_DECL ASIO_SYNC_OP_VOID store( + ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + ASIO_DECL ASIO_SYNC_OP_VOID load( + const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + type value_; + }; + + /// Serial port option to permit changing the character size. + /** + * Implements changing the character size for a given serial port. + */ + class character_size + { + public: + ASIO_DECL explicit character_size(unsigned int t = 8); + unsigned int value() const; + ASIO_DECL ASIO_SYNC_OP_VOID store( + ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + ASIO_DECL ASIO_SYNC_OP_VOID load( + const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + unsigned int value_; + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~serial_port_base() + { + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#undef ASIO_OPTION_STORAGE + +#include "asio/impl/serial_port_base.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/serial_port_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_SERIAL_PORT_BASE_HPP diff --git a/subprojects/asio/include/asio/serial_port_service.hpp b/subprojects/asio/include/asio/serial_port_service.hpp new file mode 100644 index 000000000..0e20d96e3 --- /dev/null +++ b/subprojects/asio/include/asio/serial_port_service.hpp @@ -0,0 +1,249 @@ +// +// serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SERIAL_PORT_SERVICE_HPP +#define ASIO_SERIAL_PORT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include +#include "asio/async_result.hpp" +#include "asio/detail/reactive_serial_port_service.hpp" +#include "asio/detail/win_iocp_serial_port_service.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" +#include "asio/serial_port_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a serial port. +class serial_port_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_serial_port_service service_impl_type; +#else + typedef detail::reactive_serial_port_service service_impl_type; +#endif + +public: + /// The type of a serial port implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native handle type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new serial port service for the specified io_context. + explicit serial_port_service(asio::io_context& io_context) + : asio::detail::service_base(io_context), + service_impl_(io_context) + { + } + + /// Construct a new serial port implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new serial port implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another serial port implementation. + void move_assign(implementation_type& impl, + serial_port_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a serial port implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a serial port. + ASIO_SYNC_OP_VOID open(implementation_type& impl, + const std::string& device, asio::error_code& ec) + { + service_impl_.open(impl, device, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native handle to a serial port. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) + { + service_impl_.assign(impl, handle, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a serial port implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native handle implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the handle. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set a serial port option. + template + ASIO_SYNC_OP_VOID set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + service_impl_.set_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get a serial port option. + template + ASIO_SYNC_OP_VOID get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + service_impl_.get_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Send a break sequence to the serial port. + ASIO_SYNC_OP_VOID send_break(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.send_break(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write the given data to the stream. + template + std::size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some(impl, buffers, ec); + } + + /// Start an asynchronous write. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + async_completion init(handler); + + service_impl_.async_write_some(impl, buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the stream. + template + std::size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some(impl, buffers, ec); + } + + /// Start an asynchronous read. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + async_completion init(handler); + + service_impl_.async_read_some(impl, buffers, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_SERIAL_PORT_SERVICE_HPP diff --git a/subprojects/asio/include/asio/signal_set.hpp b/subprojects/asio/include/asio/signal_set.hpp new file mode 100644 index 000000000..30e5c4e39 --- /dev/null +++ b/subprojects/asio/include/asio/signal_set.hpp @@ -0,0 +1,447 @@ +// +// signal_set.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SIGNAL_SET_HPP +#define ASIO_SIGNAL_SET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/async_result.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/basic_signal_set.hpp" +#else // defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/detail/signal_set_service.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +namespace asio { + +#if defined(ASIO_ENABLE_OLD_SERVICES) +// Typedef for the typical usage of a signal set. +typedef basic_signal_set<> signal_set; +#else // defined(ASIO_ENABLE_OLD_SERVICES) +/// Provides signal functionality. +/** + * The signal_set class provides the ability to perform an asynchronous wait + * for one or more signals to occur. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * Performing an asynchronous wait: + * @code + * void handler( + * const asio::error_code& error, + * int signal_number) + * { + * if (!error) + * { + * // A signal occurred. + * } + * } + * + * ... + * + * // Construct a signal set registered for process termination. + * asio::signal_set signals(io_context, SIGINT, SIGTERM); + * + * // Start an asynchronous wait for one of the signals to occur. + * signals.async_wait(handler); + * @endcode + * + * @par Queueing of signal notifications + * + * If a signal is registered with a signal_set, and the signal occurs when + * there are no waiting handlers, then the signal notification is queued. The + * next async_wait operation on that signal_set will dequeue the notification. + * If multiple notifications are queued, subsequent async_wait operations + * dequeue them one at a time. Signal notifications are dequeued in order of + * ascending signal number. + * + * If a signal number is removed from a signal_set (using the @c remove or @c + * erase member functions) then any queued notifications for that signal are + * discarded. + * + * @par Multiple registration of signals + * + * The same signal number may be registered with different signal_set objects. + * When the signal occurs, one handler is called for each signal_set object. + * + * Note that multiple registration only works for signals that are registered + * using Asio. The application must not also register a signal handler using + * functions such as @c signal() or @c sigaction(). + * + * @par Signal masking on POSIX platforms + * + * POSIX allows signals to be blocked using functions such as @c sigprocmask() + * and @c pthread_sigmask(). For signals to be delivered, programs must ensure + * that any signals registered using signal_set objects are unblocked in at + * least one thread. + */ +class signal_set + : ASIO_SVC_ACCESS basic_io_object +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// Construct a signal set without adding any signals. + /** + * This constructor creates a signal set without registering for any signals. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + */ + explicit signal_set(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct a signal set and add one signal. + /** + * This constructor creates a signal set and registers for one signal. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + * + * @param signal_number_1 The signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(io_context); + * signals.add(signal_number_1); @endcode + */ + signal_set(asio::io_context& io_context, int signal_number_1) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add two signals. + /** + * This constructor creates a signal set and registers for two signals. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(io_context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); @endcode + */ + signal_set(asio::io_context& io_context, int signal_number_1, + int signal_number_2) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + this->get_service().add(this->get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add three signals. + /** + * This constructor creates a signal set and registers for three signals. + * + * @param io_context The io_context object that the signal set will use to + * dispatch handlers for any asynchronous operations performed on the set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @param signal_number_3 The third signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(io_context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); + * signals.add(signal_number_3); @endcode + */ + signal_set(asio::io_context& io_context, int signal_number_1, + int signal_number_2, int signal_number_3) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + this->get_service().add(this->get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + this->get_service().add(this->get_implementation(), signal_number_3, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Destroys the signal set. + /** + * This function destroys the signal set, cancelling any outstanding + * asynchronous wait operations associated with the signal set as if by + * calling @c cancel. + */ + ~signal_set() + { + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } + + /// Add a signal to a signal_set. + /** + * This function adds the specified signal to the set. It has no effect if the + * signal is already in the set. + * + * @param signal_number The signal to be added to the set. + * + * @throws asio::system_error Thrown on failure. + */ + void add(int signal_number) + { + asio::error_code ec; + this->get_service().add(this->get_implementation(), signal_number, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Add a signal to a signal_set. + /** + * This function adds the specified signal to the set. It has no effect if the + * signal is already in the set. + * + * @param signal_number The signal to be added to the set. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID add(int signal_number, + asio::error_code& ec) + { + this->get_service().add(this->get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove a signal from a signal_set. + /** + * This function removes the specified signal from the set. It has no effect + * if the signal is not in the set. + * + * @param signal_number The signal to be removed from the set. + * + * @throws asio::system_error Thrown on failure. + * + * @note Removes any notifications that have been queued for the specified + * signal number. + */ + void remove(int signal_number) + { + asio::error_code ec; + this->get_service().remove(this->get_implementation(), signal_number, ec); + asio::detail::throw_error(ec, "remove"); + } + + /// Remove a signal from a signal_set. + /** + * This function removes the specified signal from the set. It has no effect + * if the signal is not in the set. + * + * @param signal_number The signal to be removed from the set. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Removes any notifications that have been queued for the specified + * signal number. + */ + ASIO_SYNC_OP_VOID remove(int signal_number, + asio::error_code& ec) + { + this->get_service().remove(this->get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove all signals from a signal_set. + /** + * This function removes all signals from the set. It has no effect if the set + * is already empty. + * + * @throws asio::system_error Thrown on failure. + * + * @note Removes all queued notifications. + */ + void clear() + { + asio::error_code ec; + this->get_service().clear(this->get_implementation(), ec); + asio::detail::throw_error(ec, "clear"); + } + + /// Remove all signals from a signal_set. + /** + * This function removes all signals from the set. It has no effect if the set + * is already empty. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Removes all queued notifications. + */ + ASIO_SYNC_OP_VOID clear(asio::error_code& ec) + { + this->get_service().clear(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Cancel all operations associated with the signal set. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the signal set. The handler for each cancelled + * operation will be invoked with the asio::error::operation_aborted + * error code. + * + * Cancellation does not alter the set of registered signals. + * + * @throws asio::system_error Thrown on failure. + * + * @note If a registered signal occurred before cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all operations associated with the signal set. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the signal set. The handler for each cancelled + * operation will be invoked with the asio::error::operation_aborted + * error code. + * + * Cancellation does not alter the set of registered signals. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note If a registered signal occurred before cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous operation to wait for a signal to be delivered. + /** + * This function may be used to initiate an asynchronous wait against the + * signal set. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li One of the registered signals in the signal set occurs; or + * + * @li The signal set was cancelled, in which case the handler is passed the + * error code asio::error::operation_aborted. + * + * @param handler The handler to be called when the signal occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * int signal_number // Indicates which signal occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(SignalHandler, + void (asio::error_code, int)) + async_wait(ASIO_MOVE_ARG(SignalHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a SignalHandler. + ASIO_SIGNAL_HANDLER_CHECK(SignalHandler, handler) type_check; + + async_completion init(handler); + + this->get_service().async_wait(this->get_implementation(), + init.completion_handler); + + return init.result.get(); + } +}; +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +} // namespace asio + +#endif // ASIO_SIGNAL_SET_HPP diff --git a/subprojects/asio/include/asio/signal_set_service.hpp b/subprojects/asio/include/asio/signal_set_service.hpp new file mode 100644 index 000000000..3285beb00 --- /dev/null +++ b/subprojects/asio/include/asio/signal_set_service.hpp @@ -0,0 +1,142 @@ +// +// signal_set_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SIGNAL_SET_SERVICE_HPP +#define ASIO_SIGNAL_SET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/async_result.hpp" +#include "asio/detail/signal_set_service.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a signal set. +class signal_set_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + +public: + /// The type of a signal set implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef detail::signal_set_service::implementation_type implementation_type; +#endif + + /// Construct a new signal set service for the specified io_context. + explicit signal_set_service(asio::io_context& io_context) + : asio::detail::service_base(io_context), + service_impl_(io_context) + { + } + + /// Construct a new signal set implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a signal set implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Add a signal to a signal_set. + ASIO_SYNC_OP_VOID add(implementation_type& impl, + int signal_number, asio::error_code& ec) + { + service_impl_.add(impl, signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove a signal to a signal_set. + ASIO_SYNC_OP_VOID remove(implementation_type& impl, + int signal_number, asio::error_code& ec) + { + service_impl_.remove(impl, signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove all signals from a signal_set. + ASIO_SYNC_OP_VOID clear(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.clear(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Cancel all operations associated with the signal set. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + // Start an asynchronous operation to wait for a signal to be delivered. + template + ASIO_INITFN_RESULT_TYPE(SignalHandler, + void (asio::error_code, int)) + async_wait(implementation_type& impl, + ASIO_MOVE_ARG(SignalHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // Perform any fork-related housekeeping. + void notify_fork(asio::io_context::fork_event event) + { + service_impl_.notify_fork(event); + } + + // The platform-specific implementation. + detail::signal_set_service service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_SIGNAL_SET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/socket_acceptor_service.hpp b/subprojects/asio/include/asio/socket_acceptor_service.hpp new file mode 100644 index 000000000..cd40fe142 --- /dev/null +++ b/subprojects/asio/include/asio/socket_acceptor_service.hpp @@ -0,0 +1,372 @@ +// +// socket_acceptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SOCKET_ACCEPTOR_SERVICE_HPP +#define ASIO_SOCKET_ACCEPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/basic_socket.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a socket acceptor. +template +class socket_acceptor_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_WINDOWS_RUNTIME) + typedef detail::null_socket_service service_impl_type; +#elif defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#else + typedef detail::reactive_socket_service service_impl_type; +#endif + +public: + /// The native type of the socket acceptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native acceptor type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new socket acceptor service for the specified io_context. + explicit socket_acceptor_service(asio::io_context& io_context) + : asio::detail::service_base< + socket_acceptor_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new socket acceptor implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new socket acceptor implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another socket acceptor implementation. + void move_assign(implementation_type& impl, + socket_acceptor_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } + + // All acceptor services have access to each other's implementations. + template friend class socket_acceptor_service; + + /// Move-construct a new socket acceptor implementation from another protocol + /// type. + template + void converting_move_construct(implementation_type& impl, + socket_acceptor_service& other_service, + typename socket_acceptor_service< + Protocol1>::implementation_type& other_impl, + typename enable_if::value>::type* = 0) + { + service_impl_.template converting_move_construct( + impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a socket acceptor implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a new socket acceptor implementation. + ASIO_SYNC_OP_VOID open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + service_impl_.open(impl, protocol, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native acceptor to a socket acceptor. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_acceptor, + asio::error_code& ec) + { + service_impl_.assign(impl, protocol, native_acceptor, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the acceptor is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Cancel all asynchronous operations associated with the acceptor. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Bind the socket acceptor to the specified local endpoint. + ASIO_SYNC_OP_VOID bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + service_impl_.bind(impl, endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Place the socket acceptor into the state where it will listen for new + /// connections. + ASIO_SYNC_OP_VOID listen(implementation_type& impl, int backlog, + asio::error_code& ec) + { + service_impl_.listen(impl, backlog, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Close a socket acceptor implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying acceptor. + native_handle_type release(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.release(impl, ec); + } + + /// Get the native acceptor implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Set a socket option. + template + ASIO_SYNC_OP_VOID set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + service_impl_.set_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get a socket option. + template + ASIO_SYNC_OP_VOID get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + service_impl_.get_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + template + ASIO_SYNC_OP_VOID io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + service_impl_.io_control(impl, command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the acceptor. + bool non_blocking(const implementation_type& impl) const + { + return service_impl_.non_blocking(impl); + } + + /// Sets the non-blocking mode of the acceptor. + ASIO_SYNC_OP_VOID non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native acceptor implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return service_impl_.native_non_blocking(impl); + } + + /// Sets the non-blocking mode of the native acceptor implementation. + ASIO_SYNC_OP_VOID native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.native_non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Wait for the acceptor to become ready to read, ready to write, or to have + /// pending error conditions. + ASIO_SYNC_OP_VOID wait(implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + service_impl_.wait(impl, w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the acceptor to become ready to read, ready to + /// write, or to have pending error conditions. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, socket_base::wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, w, init.completion_handler); + + return init.result.get(); + } + + /// Accept a new connection. + template + ASIO_SYNC_OP_VOID accept(implementation_type& impl, + basic_socket& peer, + endpoint_type* peer_endpoint, asio::error_code& ec, + typename enable_if::value>::type* = 0) + { + service_impl_.accept(impl, peer, peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + +#if defined(ASIO_HAS_MOVE) + /// Accept a new connection. + typename Protocol::socket accept(implementation_type& impl, + io_context* peer_io_context, endpoint_type* peer_endpoint, + asio::error_code& ec) + { + return service_impl_.accept(impl, peer_io_context, peer_endpoint, ec); + } +#endif // defined(ASIO_HAS_MOVE) + + /// Start an asynchronous accept. + template + ASIO_INITFN_RESULT_TYPE(AcceptHandler, + void (asio::error_code)) + async_accept(implementation_type& impl, + basic_socket& peer, + endpoint_type* peer_endpoint, + ASIO_MOVE_ARG(AcceptHandler) handler, + typename enable_if::value>::type* = 0) + { + async_completion init(handler); + + service_impl_.async_accept(impl, + peer, peer_endpoint, init.completion_handler); + + return init.result.get(); + } + +#if defined(ASIO_HAS_MOVE) + /// Start an asynchronous accept. + template + ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, typename Protocol::socket)) + async_accept(implementation_type& impl, + asio::io_context* peer_io_context, endpoint_type* peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler) + { + async_completion init(handler); + + service_impl_.async_accept(impl, + peer_io_context, peer_endpoint, init.completion_handler); + + return init.result.get(); + } +#endif // defined(ASIO_HAS_MOVE) + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_SOCKET_ACCEPTOR_SERVICE_HPP diff --git a/subprojects/asio/include/asio/socket_base.hpp b/subprojects/asio/include/asio/socket_base.hpp new file mode 100644 index 000000000..87ed8408b --- /dev/null +++ b/subprojects/asio/include/asio/socket_base.hpp @@ -0,0 +1,559 @@ +// +// socket_base.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SOCKET_BASE_HPP +#define ASIO_SOCKET_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/io_control.hpp" +#include "asio/detail/socket_option.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// The socket_base class is used as a base for the basic_stream_socket and +/// basic_datagram_socket class templates so that we have a common place to +/// define the shutdown_type and enum. +class socket_base +{ +public: + /// Different ways a socket may be shutdown. + enum shutdown_type + { +#if defined(GENERATING_DOCUMENTATION) + /// Shutdown the receive side of the socket. + shutdown_receive = implementation_defined, + + /// Shutdown the send side of the socket. + shutdown_send = implementation_defined, + + /// Shutdown both send and receive on the socket. + shutdown_both = implementation_defined +#else + shutdown_receive = ASIO_OS_DEF(SHUT_RD), + shutdown_send = ASIO_OS_DEF(SHUT_WR), + shutdown_both = ASIO_OS_DEF(SHUT_RDWR) +#endif + }; + + /// Bitmask type for flags that can be passed to send and receive operations. + typedef int message_flags; + +#if defined(GENERATING_DOCUMENTATION) + /// Peek at incoming data without removing it from the input queue. + static const int message_peek = implementation_defined; + + /// Process out-of-band data. + static const int message_out_of_band = implementation_defined; + + /// Specify that the data should not be subject to routing. + static const int message_do_not_route = implementation_defined; + + /// Specifies that the data marks the end of a record. + static const int message_end_of_record = implementation_defined; +#else + ASIO_STATIC_CONSTANT(int, + message_peek = ASIO_OS_DEF(MSG_PEEK)); + ASIO_STATIC_CONSTANT(int, + message_out_of_band = ASIO_OS_DEF(MSG_OOB)); + ASIO_STATIC_CONSTANT(int, + message_do_not_route = ASIO_OS_DEF(MSG_DONTROUTE)); + ASIO_STATIC_CONSTANT(int, + message_end_of_record = ASIO_OS_DEF(MSG_EOR)); +#endif + + /// Wait types. + /** + * For use with basic_socket::wait() and basic_socket::async_wait(). + */ + enum wait_type + { + /// Wait for a socket to become ready to read. + wait_read, + + /// Wait for a socket to become ready to write. + wait_write, + + /// Wait for a socket to have error conditions pending. + wait_error + }; + + /// Socket option to permit sending of broadcast messages. + /** + * Implements the SOL_SOCKET/SO_BROADCAST socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::socket_base::broadcast option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::socket_base::broadcast option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined broadcast; +#else + typedef asio::detail::socket_option::boolean< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_BROADCAST)> + broadcast; +#endif + + /// Socket option to enable socket-level debugging. + /** + * Implements the SOL_SOCKET/SO_DEBUG socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::debug option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::debug option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined debug; +#else + typedef asio::detail::socket_option::boolean< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_DEBUG)> debug; +#endif + + /// Socket option to prevent routing, use local interfaces only. + /** + * Implements the SOL_SOCKET/SO_DONTROUTE socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::socket_base::do_not_route option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_context); + * ... + * asio::socket_base::do_not_route option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined do_not_route; +#else + typedef asio::detail::socket_option::boolean< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_DONTROUTE)> + do_not_route; +#endif + + /// Socket option to send keep-alives. + /** + * Implements the SOL_SOCKET/SO_KEEPALIVE socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::keep_alive option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::keep_alive option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined keep_alive; +#else + typedef asio::detail::socket_option::boolean< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_KEEPALIVE)> keep_alive; +#endif + + /// Socket option for the send buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_SNDBUF socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::send_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::send_buffer_size option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_buffer_size; +#else + typedef asio::detail::socket_option::integer< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_SNDBUF)> + send_buffer_size; +#endif + + /// Socket option for the send low watermark. + /** + * Implements the SOL_SOCKET/SO_SNDLOWAT socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::send_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::send_low_watermark option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_low_watermark; +#else + typedef asio::detail::socket_option::integer< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_SNDLOWAT)> + send_low_watermark; +#endif + + /// Socket option for the receive buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_RCVBUF socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::receive_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::receive_buffer_size option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_buffer_size; +#else + typedef asio::detail::socket_option::integer< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_RCVBUF)> + receive_buffer_size; +#endif + + /// Socket option for the receive low watermark. + /** + * Implements the SOL_SOCKET/SO_RCVLOWAT socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::receive_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::receive_low_watermark option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_low_watermark; +#else + typedef asio::detail::socket_option::integer< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_RCVLOWAT)> + receive_low_watermark; +#endif + + /// Socket option to allow the socket to be bound to an address that is + /// already in use. + /** + * Implements the SOL_SOCKET/SO_REUSEADDR socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::socket_base::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::socket_base::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined reuse_address; +#else + typedef asio::detail::socket_option::boolean< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_REUSEADDR)> + reuse_address; +#endif + + /// Socket option to specify whether the socket lingers on close if unsent + /// data is present. + /** + * Implements the SOL_SOCKET/SO_LINGER socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::linger option(true, 30); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::linger option; + * socket.get_option(option); + * bool is_set = option.enabled(); + * unsigned short timeout = option.timeout(); + * @endcode + * + * @par Concepts: + * Socket_Option, Linger_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined linger; +#else + typedef asio::detail::socket_option::linger< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_LINGER)> + linger; +#endif + + /// Socket option for putting received out-of-band data inline. + /** + * Implements the SOL_SOCKET/SO_OOBINLINE socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::out_of_band_inline option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::out_of_band_inline option; + * socket.get_option(option); + * bool value = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined out_of_band_inline; +#else + typedef asio::detail::socket_option::boolean< + ASIO_OS_DEF(SOL_SOCKET), ASIO_OS_DEF(SO_OOBINLINE)> + out_of_band_inline; +#endif + + /// Socket option to report aborted connections on accept. + /** + * Implements a custom socket option that determines whether or not an accept + * operation is permitted to fail with asio::error::connection_aborted. + * By default the option is false. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::socket_base::enable_connection_aborted option(true); + * acceptor.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::acceptor acceptor(io_context); + * ... + * asio::socket_base::enable_connection_aborted option; + * acceptor.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined enable_connection_aborted; +#else + typedef asio::detail::socket_option::boolean< + asio::detail::custom_socket_option_level, + asio::detail::enable_connection_aborted_option> + enable_connection_aborted; +#endif + + /// IO control command to get the amount of data that can be read without + /// blocking. + /** + * Implements the FIONREAD IO control command. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_context); + * ... + * asio::socket_base::bytes_readable command(true); + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + * + * @par Concepts: + * IO_Control_Command, Size_IO_Control_Command. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined bytes_readable; +#else + typedef asio::detail::io_control::bytes_readable bytes_readable; +#endif + + /// The maximum length of the queue of pending incoming connections. +#if defined(GENERATING_DOCUMENTATION) + static const int max_listen_connections = implementation_defined; +#else + ASIO_STATIC_CONSTANT(int, max_listen_connections + = ASIO_OS_DEF(SOMAXCONN)); +#endif + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use max_listen_connections.) The maximum length of the queue + /// of pending incoming connections. +#if defined(GENERATING_DOCUMENTATION) + static const int max_connections = implementation_defined; +#else + ASIO_STATIC_CONSTANT(int, max_connections + = ASIO_OS_DEF(SOMAXCONN)); +#endif +#endif // !defined(ASIO_NO_DEPRECATED) + +protected: + /// Protected destructor to prevent deletion through this type. + ~socket_base() + { + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SOCKET_BASE_HPP diff --git a/subprojects/asio/include/asio/spawn.hpp b/subprojects/asio/include/asio/spawn.hpp new file mode 100644 index 000000000..a91c5815f --- /dev/null +++ b/subprojects/asio/include/asio/spawn.hpp @@ -0,0 +1,336 @@ +// +// spawn.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SPAWN_HPP +#define ASIO_SPAWN_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/bind_executor.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/wrapped_handler.hpp" +#include "asio/executor.hpp" +#include "asio/io_context.hpp" +#include "asio/is_executor.hpp" +#include "asio/strand.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Context object the represents the currently executing coroutine. +/** + * The basic_yield_context class is used to represent the currently executing + * stackful coroutine. A basic_yield_context may be passed as a handler to an + * asynchronous operation. For example: + * + * @code template + * void my_coroutine(basic_yield_context yield) + * { + * ... + * std::size_t n = my_socket.async_read_some(buffer, yield); + * ... + * } @endcode + * + * The initiating function (async_read_some in the above example) suspends the + * current coroutine. The coroutine is resumed when the asynchronous operation + * completes, and the result of the operation is returned. + */ +template +class basic_yield_context +{ +public: + /// The coroutine callee type, used by the implementation. + /** + * When using Boost.Coroutine v1, this type is: + * @code typename coroutine @endcode + * When using Boost.Coroutine v2 (unidirectional coroutines), this type is: + * @code push_coroutine @endcode + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined callee_type; +#elif defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2) + typedef boost::coroutines::push_coroutine callee_type; +#else + typedef boost::coroutines::coroutine callee_type; +#endif + + /// The coroutine caller type, used by the implementation. + /** + * When using Boost.Coroutine v1, this type is: + * @code typename coroutine::caller_type @endcode + * When using Boost.Coroutine v2 (unidirectional coroutines), this type is: + * @code pull_coroutine @endcode + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined caller_type; +#elif defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2) + typedef boost::coroutines::pull_coroutine caller_type; +#else + typedef boost::coroutines::coroutine::caller_type caller_type; +#endif + + /// Construct a yield context to represent the specified coroutine. + /** + * Most applications do not need to use this constructor. Instead, the + * spawn() function passes a yield context as an argument to the coroutine + * function. + */ + basic_yield_context( + const detail::weak_ptr& coro, + caller_type& ca, Handler& handler) + : coro_(coro), + ca_(ca), + handler_(handler), + ec_(0) + { + } + + /// Construct a yield context from another yield context type. + /** + * Requires that OtherHandler be convertible to Handler. + */ + template + basic_yield_context(const basic_yield_context& other) + : coro_(other.coro_), + ca_(other.ca_), + handler_(other.handler_), + ec_(other.ec_) + { + } + + /// Return a yield context that sets the specified error_code. + /** + * By default, when a yield context is used with an asynchronous operation, a + * non-success error_code is converted to system_error and thrown. This + * operator may be used to specify an error_code object that should instead be + * set with the asynchronous operation's result. For example: + * + * @code template + * void my_coroutine(basic_yield_context yield) + * { + * ... + * std::size_t n = my_socket.async_read_some(buffer, yield[ec]); + * if (ec) + * { + * // An error occurred. + * } + * ... + * } @endcode + */ + basic_yield_context operator[](asio::error_code& ec) const + { + basic_yield_context tmp(*this); + tmp.ec_ = &ec; + return tmp; + } + +#if defined(GENERATING_DOCUMENTATION) +private: +#endif // defined(GENERATING_DOCUMENTATION) + detail::weak_ptr coro_; + caller_type& ca_; + Handler handler_; + asio::error_code* ec_; +}; + +#if defined(GENERATING_DOCUMENTATION) +/// Context object that represents the currently executing coroutine. +typedef basic_yield_context yield_context; +#else // defined(GENERATING_DOCUMENTATION) +typedef basic_yield_context< + executor_binder > yield_context; +#endif // defined(GENERATING_DOCUMENTATION) + +/** + * @defgroup spawn asio::spawn + * + * @brief Start a new stackful coroutine. + * + * The spawn() function is a high-level wrapper over the Boost.Coroutine + * library. This function enables programs to implement asynchronous logic in a + * synchronous manner, as illustrated by the following example: + * + * @code asio::spawn(my_strand, do_echo); + * + * // ... + * + * void do_echo(asio::yield_context yield) + * { + * try + * { + * char data[128]; + * for (;;) + * { + * std::size_t length = + * my_socket.async_read_some( + * asio::buffer(data), yield); + * + * asio::async_write(my_socket, + * asio::buffer(data, length), yield); + * } + * } + * catch (std::exception& e) + * { + * // ... + * } + * } @endcode + */ +/*@{*/ + +/// Start a new stackful coroutine, calling the specified handler when it +/// completes. +/** + * This function is used to launch a new coroutine. + * + * @param function The coroutine function. The function must have the signature: + * @code void function(basic_yield_context yield); @endcode + * + * @param attributes Boost.Coroutine attributes used to customise the coroutine. + */ +template +void spawn(ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes + = boost::coroutines::attributes()); + +/// Start a new stackful coroutine, calling the specified handler when it +/// completes. +/** + * This function is used to launch a new coroutine. + * + * @param handler A handler to be called when the coroutine exits. More + * importantly, the handler provides an execution context (via the the handler + * invocation hook) for the coroutine. The handler must have the signature: + * @code void handler(); @endcode + * + * @param function The coroutine function. The function must have the signature: + * @code void function(basic_yield_context yield); @endcode + * + * @param attributes Boost.Coroutine attributes used to customise the coroutine. + */ +template +void spawn(ASIO_MOVE_ARG(Handler) handler, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes + = boost::coroutines::attributes(), + typename enable_if::type>::value && + !is_convertible::value>::type* = 0); + +/// Start a new stackful coroutine, inheriting the execution context of another. +/** + * This function is used to launch a new coroutine. + * + * @param ctx Identifies the current coroutine as a parent of the new + * coroutine. This specifies that the new coroutine should inherit the + * execution context of the parent. For example, if the parent coroutine is + * executing in a particular strand, then the new coroutine will execute in the + * same strand. + * + * @param function The coroutine function. The function must have the signature: + * @code void function(basic_yield_context yield); @endcode + * + * @param attributes Boost.Coroutine attributes used to customise the coroutine. + */ +template +void spawn(basic_yield_context ctx, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes + = boost::coroutines::attributes()); + +/// Start a new stackful coroutine that executes on a given executor. +/** + * This function is used to launch a new coroutine. + * + * @param ex Identifies the executor that will run the coroutine. The new + * coroutine is implicitly given its own strand within this executor. + * + * @param function The coroutine function. The function must have the signature: + * @code void function(yield_context yield); @endcode + * + * @param attributes Boost.Coroutine attributes used to customise the coroutine. + */ +template +void spawn(const Executor& ex, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes + = boost::coroutines::attributes(), + typename enable_if::value>::type* = 0); + +/// Start a new stackful coroutine that executes on a given strand. +/** + * This function is used to launch a new coroutine. + * + * @param ex Identifies the strand that will run the coroutine. + * + * @param function The coroutine function. The function must have the signature: + * @code void function(yield_context yield); @endcode + * + * @param attributes Boost.Coroutine attributes used to customise the coroutine. + */ +template +void spawn(const strand& ex, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes + = boost::coroutines::attributes()); + +/// Start a new stackful coroutine that executes in the context of a strand. +/** + * This function is used to launch a new coroutine. + * + * @param s Identifies a strand. By starting multiple coroutines on the same + * strand, the implementation ensures that none of those coroutines can execute + * simultaneously. + * + * @param function The coroutine function. The function must have the signature: + * @code void function(yield_context yield); @endcode + * + * @param attributes Boost.Coroutine attributes used to customise the coroutine. + */ +template +void spawn(const asio::io_context::strand& s, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes + = boost::coroutines::attributes()); + +/// Start a new stackful coroutine that executes on a given execution context. +/** + * This function is used to launch a new coroutine. + * + * @param ctx Identifies the execution context that will run the coroutine. The + * new coroutine is implicitly given its own strand within this execution + * context. + * + * @param function The coroutine function. The function must have the signature: + * @code void function(yield_context yield); @endcode + * + * @param attributes Boost.Coroutine attributes used to customise the coroutine. + */ +template +void spawn(ExecutionContext& ctx, + ASIO_MOVE_ARG(Function) function, + const boost::coroutines::attributes& attributes + = boost::coroutines::attributes(), + typename enable_if::value>::type* = 0); + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/spawn.hpp" + +#endif // ASIO_SPAWN_HPP diff --git a/subprojects/asio/include/asio/ssl.hpp b/subprojects/asio/include/asio/ssl.hpp new file mode 100644 index 000000000..cbad19d81 --- /dev/null +++ b/subprojects/asio/include/asio/ssl.hpp @@ -0,0 +1,27 @@ +// +// ssl.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_HPP +#define ASIO_SSL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/ssl/context.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/error.hpp" +#include "asio/ssl/rfc2818_verification.hpp" +#include "asio/ssl/stream.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/verify_context.hpp" +#include "asio/ssl/verify_mode.hpp" + +#endif // ASIO_SSL_HPP diff --git a/subprojects/asio/include/asio/ssl/context.hpp b/subprojects/asio/include/asio/ssl/context.hpp new file mode 100644 index 000000000..9543aab68 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/context.hpp @@ -0,0 +1,758 @@ +// +// ssl/context.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_CONTEXT_HPP +#define ASIO_SSL_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/buffer.hpp" +#include "asio/io_context.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/detail/openssl_types.hpp" +#include "asio/ssl/detail/openssl_init.hpp" +#include "asio/ssl/detail/password_callback.hpp" +#include "asio/ssl/detail/verify_callback.hpp" +#include "asio/ssl/verify_mode.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +class context + : public context_base, + private noncopyable +{ +public: + /// The native handle type of the SSL context. + typedef SSL_CTX* native_handle_type; + + /// Constructor. + ASIO_DECL explicit context(method m); + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a context from another. + /** + * This constructor moves an SSL context from one object to another. + * + * @param other The other context object from which the move will occur. + * + * @note Following the move, the following operations only are valid for the + * moved-from object: + * @li Destruction. + * @li As a target for move-assignment. + */ + ASIO_DECL context(context&& other); + + /// Move-assign a context from another. + /** + * This assignment operator moves an SSL context from one object to another. + * + * @param other The other context object from which the move will occur. + * + * @note Following the move, the following operations only are valid for the + * moved-from object: + * @li Destruction. + * @li As a target for move-assignment. + */ + ASIO_DECL context& operator=(context&& other); +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor. + ASIO_DECL ~context(); + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to context functionality that is + * not otherwise provided. + */ + ASIO_DECL native_handle_type native_handle(); + + /// Clear options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The specified options, if currently enabled on the + * context, are cleared. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_clear_options. + */ + ASIO_DECL void clear_options(options o); + + /// Clear options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The specified options, if currently enabled on the + * context, are cleared. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_clear_options. + */ + ASIO_DECL ASIO_SYNC_OP_VOID clear_options(options o, + asio::error_code& ec); + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_options. + */ + ASIO_DECL void set_options(options o); + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_options. + */ + ASIO_DECL ASIO_SYNC_OP_VOID set_options(options o, + asio::error_code& ec); + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. See @ref verify_mode for + * available values. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_verify. + */ + ASIO_DECL void set_verify_mode(verify_mode v); + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. See @ref verify_mode for + * available values. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_verify. + */ + ASIO_DECL ASIO_SYNC_OP_VOID set_verify_mode( + verify_mode v, asio::error_code& ec); + + /// Set the peer verification depth. + /** + * This function may be used to configure the maximum verification depth + * allowed by the context. + * + * @param depth Maximum depth for the certificate chain verification that + * shall be allowed. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_verify_depth. + */ + ASIO_DECL void set_verify_depth(int depth); + + /// Set the peer verification depth. + /** + * This function may be used to configure the maximum verification depth + * allowed by the context. + * + * @param depth Maximum depth for the certificate chain verification that + * shall be allowed. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_verify_depth. + */ + ASIO_DECL ASIO_SYNC_OP_VOID set_verify_depth( + int depth, asio::error_code& ec); + + /// Set the callback used to verify peer certificates. + /** + * This function is used to specify a callback function that will be called + * by the implementation when it needs to verify a peer certificate. + * + * @param callback The function object to be used for verifying a certificate. + * The function signature of the handler must be: + * @code bool verify_callback( + * bool preverified, // True if the certificate passed pre-verification. + * verify_context& ctx // The peer certificate and other context. + * ); @endcode + * The return value of the callback is true if the certificate has passed + * verification, false otherwise. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_verify. + */ + template + void set_verify_callback(VerifyCallback callback); + + /// Set the callback used to verify peer certificates. + /** + * This function is used to specify a callback function that will be called + * by the implementation when it needs to verify a peer certificate. + * + * @param callback The function object to be used for verifying a certificate. + * The function signature of the handler must be: + * @code bool verify_callback( + * bool preverified, // True if the certificate passed pre-verification. + * verify_context& ctx // The peer certificate and other context. + * ); @endcode + * The return value of the callback is true if the certificate has passed + * verification, false otherwise. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_verify. + */ + template + ASIO_SYNC_OP_VOID set_verify_callback(VerifyCallback callback, + asio::error_code& ec); + + /// Load a certification authority file for performing verification. + /** + * This function is used to load one or more trusted certification authorities + * from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_load_verify_locations. + */ + ASIO_DECL void load_verify_file(const std::string& filename); + + /// Load a certification authority file for performing verification. + /** + * This function is used to load the certificates for one or more trusted + * certification authorities from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_load_verify_locations. + */ + ASIO_DECL ASIO_SYNC_OP_VOID load_verify_file( + const std::string& filename, asio::error_code& ec); + + /// Add certification authority for performing verification. + /** + * This function is used to add one trusted certification authority + * from a memory buffer. + * + * @param ca The buffer containing the certification authority certificate. + * The certificate must use the PEM format. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_get_cert_store and @c X509_STORE_add_cert. + */ + ASIO_DECL void add_certificate_authority(const const_buffer& ca); + + /// Add certification authority for performing verification. + /** + * This function is used to add one trusted certification authority + * from a memory buffer. + * + * @param ca The buffer containing the certification authority certificate. + * The certificate must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_get_cert_store and @c X509_STORE_add_cert. + */ + ASIO_DECL ASIO_SYNC_OP_VOID add_certificate_authority( + const const_buffer& ca, asio::error_code& ec); + + /// Configures the context to use the default directories for finding + /// certification authority certificates. + /** + * This function specifies that the context should use the default, + * system-dependent directories for locating certification authority + * certificates. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_default_verify_paths. + */ + ASIO_DECL void set_default_verify_paths(); + + /// Configures the context to use the default directories for finding + /// certification authority certificates. + /** + * This function specifies that the context should use the default, + * system-dependent directories for locating certification authority + * certificates. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_default_verify_paths. + */ + ASIO_DECL ASIO_SYNC_OP_VOID set_default_verify_paths( + asio::error_code& ec); + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_load_verify_locations. + */ + ASIO_DECL void add_verify_path(const std::string& path); + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_load_verify_locations. + */ + ASIO_DECL ASIO_SYNC_OP_VOID add_verify_path( + const std::string& path, asio::error_code& ec); + + /// Use a certificate from a memory buffer. + /** + * This function is used to load a certificate into the context from a buffer. + * + * @param certificate The buffer containing the certificate. + * + * @param format The certificate format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_certificate or SSL_CTX_use_certificate_ASN1. + */ + ASIO_DECL void use_certificate( + const const_buffer& certificate, file_format format); + + /// Use a certificate from a memory buffer. + /** + * This function is used to load a certificate into the context from a buffer. + * + * @param certificate The buffer containing the certificate. + * + * @param format The certificate format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_certificate or SSL_CTX_use_certificate_ASN1. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_certificate( + const const_buffer& certificate, file_format format, + asio::error_code& ec); + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_certificate_file. + */ + ASIO_DECL void use_certificate_file( + const std::string& filename, file_format format); + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_certificate_file. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_certificate_file( + const std::string& filename, file_format format, + asio::error_code& ec); + + /// Use a certificate chain from a memory buffer. + /** + * This function is used to load a certificate chain into the context from a + * buffer. + * + * @param chain The buffer containing the certificate chain. The certificate + * chain must use the PEM format. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_certificate and SSL_CTX_add_extra_chain_cert. + */ + ASIO_DECL void use_certificate_chain(const const_buffer& chain); + + /// Use a certificate chain from a memory buffer. + /** + * This function is used to load a certificate chain into the context from a + * buffer. + * + * @param chain The buffer containing the certificate chain. The certificate + * chain must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_certificate and SSL_CTX_add_extra_chain_cert. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_certificate_chain( + const const_buffer& chain, asio::error_code& ec); + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_certificate_chain_file. + */ + ASIO_DECL void use_certificate_chain_file(const std::string& filename); + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_certificate_chain_file. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_certificate_chain_file( + const std::string& filename, asio::error_code& ec); + + /// Use a private key from a memory buffer. + /** + * This function is used to load a private key into the context from a buffer. + * + * @param private_key The buffer containing the private key. + * + * @param format The private key format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_PrivateKey or SSL_CTX_use_PrivateKey_ASN1. + */ + ASIO_DECL void use_private_key( + const const_buffer& private_key, file_format format); + + /// Use a private key from a memory buffer. + /** + * This function is used to load a private key into the context from a buffer. + * + * @param private_key The buffer containing the private key. + * + * @param format The private key format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_PrivateKey or SSL_CTX_use_PrivateKey_ASN1. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_private_key( + const const_buffer& private_key, file_format format, + asio::error_code& ec); + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_PrivateKey_file. + */ + ASIO_DECL void use_private_key_file( + const std::string& filename, file_format format); + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_PrivateKey_file. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_private_key_file( + const std::string& filename, file_format format, + asio::error_code& ec); + + /// Use an RSA private key from a memory buffer. + /** + * This function is used to load an RSA private key into the context from a + * buffer. + * + * @param private_key The buffer containing the RSA private key. + * + * @param format The private key format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_RSAPrivateKey or SSL_CTX_use_RSAPrivateKey_ASN1. + */ + ASIO_DECL void use_rsa_private_key( + const const_buffer& private_key, file_format format); + + /// Use an RSA private key from a memory buffer. + /** + * This function is used to load an RSA private key into the context from a + * buffer. + * + * @param private_key The buffer containing the RSA private key. + * + * @param format The private key format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_RSAPrivateKey or SSL_CTX_use_RSAPrivateKey_ASN1. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_rsa_private_key( + const const_buffer& private_key, file_format format, + asio::error_code& ec); + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_use_RSAPrivateKey_file. + */ + ASIO_DECL void use_rsa_private_key_file( + const std::string& filename, file_format format); + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_use_RSAPrivateKey_file. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_rsa_private_key_file( + const std::string& filename, file_format format, + asio::error_code& ec); + + /// Use the specified memory buffer to obtain the temporary Diffie-Hellman + /// parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a buffer. + * + * @param dh The memory buffer containing the Diffie-Hellman parameters. The + * buffer must use the PEM format. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_tmp_dh. + */ + ASIO_DECL void use_tmp_dh(const const_buffer& dh); + + /// Use the specified memory buffer to obtain the temporary Diffie-Hellman + /// parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a buffer. + * + * @param dh The memory buffer containing the Diffie-Hellman parameters. The + * buffer must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_tmp_dh. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_tmp_dh( + const const_buffer& dh, asio::error_code& ec); + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_tmp_dh. + */ + ASIO_DECL void use_tmp_dh_file(const std::string& filename); + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_tmp_dh. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_tmp_dh_file( + const std::string& filename, asio::error_code& ec); + + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_CTX_set_default_passwd_cb. + */ + template + void set_password_callback(PasswordCallback callback); + + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_default_passwd_cb. + */ + template + ASIO_SYNC_OP_VOID set_password_callback(PasswordCallback callback, + asio::error_code& ec); + +private: + struct bio_cleanup; + struct x509_cleanup; + struct evp_pkey_cleanup; + struct rsa_cleanup; + struct dh_cleanup; + + // Helper function used to set a peer certificate verification callback. + ASIO_DECL ASIO_SYNC_OP_VOID do_set_verify_callback( + detail::verify_callback_base* callback, asio::error_code& ec); + + // Callback used when the SSL implementation wants to verify a certificate. + ASIO_DECL static int verify_callback_function( + int preverified, X509_STORE_CTX* ctx); + + // Helper function used to set a password callback. + ASIO_DECL ASIO_SYNC_OP_VOID do_set_password_callback( + detail::password_callback_base* callback, asio::error_code& ec); + + // Callback used when the SSL implementation wants a password. + ASIO_DECL static int password_callback_function( + char* buf, int size, int purpose, void* data); + + // Helper function to set the temporary Diffie-Hellman parameters from a BIO. + ASIO_DECL ASIO_SYNC_OP_VOID do_use_tmp_dh( + BIO* bio, asio::error_code& ec); + + // Helper function to make a BIO from a memory buffer. + ASIO_DECL BIO* make_buffer_bio(const const_buffer& b); + + // The underlying native implementation. + native_handle_type handle_; + + // Ensure openssl is initialised. + asio::ssl::detail::openssl_init<> init_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/ssl/impl/context.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ssl/impl/context.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_SSL_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/ssl/context_base.hpp b/subprojects/asio/include/asio/ssl/context_base.hpp new file mode 100644 index 000000000..b862f9682 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/context_base.hpp @@ -0,0 +1,209 @@ +// +// ssl/context_base.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_CONTEXT_BASE_HPP +#define ASIO_SSL_CONTEXT_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +/// The context_base class is used as a base for the basic_context class +/// template so that we have a common place to define various enums. +class context_base +{ +public: + /// Different methods supported by a context. + enum method + { + /// Generic SSL version 2. + sslv2, + + /// SSL version 2 client. + sslv2_client, + + /// SSL version 2 server. + sslv2_server, + + /// Generic SSL version 3. + sslv3, + + /// SSL version 3 client. + sslv3_client, + + /// SSL version 3 server. + sslv3_server, + + /// Generic TLS version 1. + tlsv1, + + /// TLS version 1 client. + tlsv1_client, + + /// TLS version 1 server. + tlsv1_server, + + /// Generic SSL/TLS. + sslv23, + + /// SSL/TLS client. + sslv23_client, + + /// SSL/TLS server. + sslv23_server, + + /// Generic TLS version 1.1. + tlsv11, + + /// TLS version 1.1 client. + tlsv11_client, + + /// TLS version 1.1 server. + tlsv11_server, + + /// Generic TLS version 1.2. + tlsv12, + + /// TLS version 1.2 client. + tlsv12_client, + + /// TLS version 1.2 server. + tlsv12_server, + + /// Generic TLS version 1.3. + tlsv13, + + /// TLS version 1.3 client. + tlsv13_client, + + /// TLS version 1.3 server. + tlsv13_server, + + /// Generic TLS. + tls, + + /// TLS client. + tls_client, + + /// TLS server. + tls_server + }; + + /// Bitmask type for SSL options. + typedef long options; + +#if defined(GENERATING_DOCUMENTATION) + /// Implement various bug workarounds. + static const long default_workarounds = implementation_defined; + + /// Always create a new key when using tmp_dh parameters. + static const long single_dh_use = implementation_defined; + + /// Disable SSL v2. + static const long no_sslv2 = implementation_defined; + + /// Disable SSL v3. + static const long no_sslv3 = implementation_defined; + + /// Disable TLS v1. + static const long no_tlsv1 = implementation_defined; + + /// Disable TLS v1.1. + static const long no_tlsv1_1 = implementation_defined; + + /// Disable TLS v1.2. + static const long no_tlsv1_2 = implementation_defined; + + /// Disable TLS v1.3. + static const long no_tlsv1_3 = implementation_defined; + + /// Disable compression. Compression is disabled by default. + static const long no_compression = implementation_defined; +#else + ASIO_STATIC_CONSTANT(long, default_workarounds = SSL_OP_ALL); + ASIO_STATIC_CONSTANT(long, single_dh_use = SSL_OP_SINGLE_DH_USE); + ASIO_STATIC_CONSTANT(long, no_sslv2 = SSL_OP_NO_SSLv2); + ASIO_STATIC_CONSTANT(long, no_sslv3 = SSL_OP_NO_SSLv3); + ASIO_STATIC_CONSTANT(long, no_tlsv1 = SSL_OP_NO_TLSv1); +# if defined(SSL_OP_NO_TLSv1_1) + ASIO_STATIC_CONSTANT(long, no_tlsv1_1 = SSL_OP_NO_TLSv1_1); +# else // defined(SSL_OP_NO_TLSv1_1) + ASIO_STATIC_CONSTANT(long, no_tlsv1_1 = 0x10000000L); +# endif // defined(SSL_OP_NO_TLSv1_1) +# if defined(SSL_OP_NO_TLSv1_2) + ASIO_STATIC_CONSTANT(long, no_tlsv1_2 = SSL_OP_NO_TLSv1_2); +# else // defined(SSL_OP_NO_TLSv1_2) + ASIO_STATIC_CONSTANT(long, no_tlsv1_2 = 0x08000000L); +# endif // defined(SSL_OP_NO_TLSv1_2) +# if defined(SSL_OP_NO_TLSv1_3) + ASIO_STATIC_CONSTANT(long, no_tlsv1_3 = SSL_OP_NO_TLSv1_3); +# else // defined(SSL_OP_NO_TLSv1_3) + ASIO_STATIC_CONSTANT(long, no_tlsv1_3 = 0x20000000L); +# endif // defined(SSL_OP_NO_TLSv1_3) +# if defined(SSL_OP_NO_COMPRESSION) + ASIO_STATIC_CONSTANT(long, no_compression = SSL_OP_NO_COMPRESSION); +# else // defined(SSL_OP_NO_COMPRESSION) + ASIO_STATIC_CONSTANT(long, no_compression = 0x20000L); +# endif // defined(SSL_OP_NO_COMPRESSION) +#endif + + /// File format types. + enum file_format + { + /// ASN.1 file. + asn1, + + /// PEM file. + pem + }; + +#if !defined(GENERATING_DOCUMENTATION) + // The following types and constants are preserved for backward compatibility. + // New programs should use the equivalents of the same names that are defined + // in the asio::ssl namespace. + typedef int verify_mode; + ASIO_STATIC_CONSTANT(int, verify_none = SSL_VERIFY_NONE); + ASIO_STATIC_CONSTANT(int, verify_peer = SSL_VERIFY_PEER); + ASIO_STATIC_CONSTANT(int, + verify_fail_if_no_peer_cert = SSL_VERIFY_FAIL_IF_NO_PEER_CERT); + ASIO_STATIC_CONSTANT(int, verify_client_once = SSL_VERIFY_CLIENT_ONCE); +#endif + + /// Purpose of PEM password. + enum password_purpose + { + /// The password is needed for reading/decryption. + for_reading, + + /// The password is needed for writing/encryption. + for_writing + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~context_base() + { + } +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_CONTEXT_BASE_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/buffered_handshake_op.hpp b/subprojects/asio/include/asio/ssl/detail/buffered_handshake_op.hpp new file mode 100644 index 000000000..38a03fcce --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/buffered_handshake_op.hpp @@ -0,0 +1,114 @@ +// +// ssl/detail/buffered_handshake_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_BUFFERED_HANDSHAKE_OP_HPP +#define ASIO_SSL_DETAIL_BUFFERED_HANDSHAKE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/ssl/detail/engine.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +template +class buffered_handshake_op +{ +public: + buffered_handshake_op(stream_base::handshake_type type, + const ConstBufferSequence& buffers) + : type_(type), + buffers_(buffers), + total_buffer_size_(asio::buffer_size(buffers_)) + { + } + + engine::want operator()(engine& eng, + asio::error_code& ec, + std::size_t& bytes_transferred) const + { + return this->process(eng, ec, bytes_transferred, + asio::buffer_sequence_begin(buffers_), + asio::buffer_sequence_end(buffers_)); + } + + template + void call_handler(Handler& handler, + const asio::error_code& ec, + const std::size_t& bytes_transferred) const + { + handler(ec, bytes_transferred); + } + +private: + template + engine::want process(engine& eng, + asio::error_code& ec, + std::size_t& bytes_transferred, + Iterator begin, Iterator end) const + { + Iterator iter = begin; + std::size_t accumulated_size = 0; + + for (;;) + { + engine::want want = eng.handshake(type_, ec); + if (want != engine::want_input_and_retry + || bytes_transferred == total_buffer_size_) + return want; + + // Find the next buffer piece to be fed to the engine. + while (iter != end) + { + const_buffer buffer(*iter); + + // Skip over any buffers which have already been consumed by the engine. + if (bytes_transferred >= accumulated_size + buffer.size()) + { + accumulated_size += buffer.size(); + ++iter; + continue; + } + + // The current buffer may have been partially consumed by the engine on + // a previous iteration. If so, adjust the buffer to point to the + // unused portion. + if (bytes_transferred > accumulated_size) + buffer = buffer + (bytes_transferred - accumulated_size); + + // Pass the buffer to the engine, and update the bytes transferred to + // reflect the total number of bytes consumed so far. + bytes_transferred += buffer.size(); + buffer = eng.put_input(buffer); + bytes_transferred -= buffer.size(); + break; + } + } + } + + stream_base::handshake_type type_; + ConstBufferSequence buffers_; + std::size_t total_buffer_size_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_BUFFERED_HANDSHAKE_OP_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/engine.hpp b/subprojects/asio/include/asio/ssl/detail/engine.hpp new file mode 100644 index 000000000..2f033d66c --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/engine.hpp @@ -0,0 +1,160 @@ +// +// ssl/detail/engine.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_ENGINE_HPP +#define ASIO_SSL_DETAIL_ENGINE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/buffer.hpp" +#include "asio/detail/static_mutex.hpp" +#include "asio/ssl/detail/openssl_types.hpp" +#include "asio/ssl/detail/verify_callback.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/verify_mode.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class engine +{ +public: + enum want + { + // Returned by functions to indicate that the engine wants input. The input + // buffer should be updated to point to the data. The engine then needs to + // be called again to retry the operation. + want_input_and_retry = -2, + + // Returned by functions to indicate that the engine wants to write output. + // The output buffer points to the data to be written. The engine then + // needs to be called again to retry the operation. + want_output_and_retry = -1, + + // Returned by functions to indicate that the engine doesn't need input or + // output. + want_nothing = 0, + + // Returned by functions to indicate that the engine wants to write output. + // The output buffer points to the data to be written. After that the + // operation is complete, and the engine does not need to be called again. + want_output = 1 + }; + + // Construct a new engine for the specified context. + ASIO_DECL explicit engine(SSL_CTX* context); + + // Destructor. + ASIO_DECL ~engine(); + + // Get the underlying implementation in the native type. + ASIO_DECL SSL* native_handle(); + + // Set the peer verification mode. + ASIO_DECL asio::error_code set_verify_mode( + verify_mode v, asio::error_code& ec); + + // Set the peer verification depth. + ASIO_DECL asio::error_code set_verify_depth( + int depth, asio::error_code& ec); + + // Set a peer certificate verification callback. + ASIO_DECL asio::error_code set_verify_callback( + verify_callback_base* callback, asio::error_code& ec); + + // Perform an SSL handshake using either SSL_connect (client-side) or + // SSL_accept (server-side). + ASIO_DECL want handshake( + stream_base::handshake_type type, asio::error_code& ec); + + // Perform a graceful shutdown of the SSL session. + ASIO_DECL want shutdown(asio::error_code& ec); + + // Write bytes to the SSL session. + ASIO_DECL want write(const asio::const_buffer& data, + asio::error_code& ec, std::size_t& bytes_transferred); + + // Read bytes from the SSL session. + ASIO_DECL want read(const asio::mutable_buffer& data, + asio::error_code& ec, std::size_t& bytes_transferred); + + // Get output data to be written to the transport. + ASIO_DECL asio::mutable_buffer get_output( + const asio::mutable_buffer& data); + + // Put input data that was read from the transport. + ASIO_DECL asio::const_buffer put_input( + const asio::const_buffer& data); + + // Map an error::eof code returned by the underlying transport according to + // the type and state of the SSL session. Returns a const reference to the + // error code object, suitable for passing to a completion handler. + ASIO_DECL const asio::error_code& map_error_code( + asio::error_code& ec) const; + +private: + // Disallow copying and assignment. + engine(const engine&); + engine& operator=(const engine&); + + // Callback used when the SSL implementation wants to verify a certificate. + ASIO_DECL static int verify_callback_function( + int preverified, X509_STORE_CTX* ctx); + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + // The SSL_accept function may not be thread safe. This mutex is used to + // protect all calls to the SSL_accept function. + ASIO_DECL static asio::detail::static_mutex& accept_mutex(); +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) + + // Perform one operation. Returns >= 0 on success or error, want_read if the + // operation needs more input, or want_write if it needs to write some output + // before the operation can complete. + ASIO_DECL want perform(int (engine::* op)(void*, std::size_t), + void* data, std::size_t length, asio::error_code& ec, + std::size_t* bytes_transferred); + + // Adapt the SSL_accept function to the signature needed for perform(). + ASIO_DECL int do_accept(void*, std::size_t); + + // Adapt the SSL_connect function to the signature needed for perform(). + ASIO_DECL int do_connect(void*, std::size_t); + + // Adapt the SSL_shutdown function to the signature needed for perform(). + ASIO_DECL int do_shutdown(void*, std::size_t); + + // Adapt the SSL_read function to the signature needed for perform(). + ASIO_DECL int do_read(void* data, std::size_t length); + + // Adapt the SSL_write function to the signature needed for perform(). + ASIO_DECL int do_write(void* data, std::size_t length); + + SSL* ssl_; + BIO* ext_bio_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/ssl/detail/impl/engine.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_SSL_DETAIL_ENGINE_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/handshake_op.hpp b/subprojects/asio/include/asio/ssl/detail/handshake_op.hpp new file mode 100644 index 000000000..f782023a1 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/handshake_op.hpp @@ -0,0 +1,62 @@ +// +// ssl/detail/handshake_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_HANDSHAKE_OP_HPP +#define ASIO_SSL_DETAIL_HANDSHAKE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/ssl/detail/engine.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class handshake_op +{ +public: + handshake_op(stream_base::handshake_type type) + : type_(type) + { + } + + engine::want operator()(engine& eng, + asio::error_code& ec, + std::size_t& bytes_transferred) const + { + bytes_transferred = 0; + return eng.handshake(type_, ec); + } + + template + void call_handler(Handler& handler, + const asio::error_code& ec, + const std::size_t&) const + { + handler(ec); + } + +private: + stream_base::handshake_type type_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_HANDSHAKE_OP_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/impl/engine.ipp b/subprojects/asio/include/asio/ssl/detail/impl/engine.ipp new file mode 100644 index 000000000..e60e8d6f8 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/impl/engine.ipp @@ -0,0 +1,322 @@ +// +// ssl/detail/impl/engine.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_IMPL_ENGINE_IPP +#define ASIO_SSL_DETAIL_IMPL_ENGINE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/ssl/detail/engine.hpp" +#include "asio/ssl/error.hpp" +#include "asio/ssl/verify_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +engine::engine(SSL_CTX* context) + : ssl_(::SSL_new(context)) +{ + if (!ssl_) + { + asio::error_code ec( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + asio::detail::throw_error(ec, "engine"); + } + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + accept_mutex().init(); +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) + + ::SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE); + ::SSL_set_mode(ssl_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#if defined(SSL_MODE_RELEASE_BUFFERS) + ::SSL_set_mode(ssl_, SSL_MODE_RELEASE_BUFFERS); +#endif // defined(SSL_MODE_RELEASE_BUFFERS) + + ::BIO* int_bio = 0; + ::BIO_new_bio_pair(&int_bio, 0, &ext_bio_, 0); + ::SSL_set_bio(ssl_, int_bio, int_bio); +} + +engine::~engine() +{ + if (SSL_get_app_data(ssl_)) + { + delete static_cast(SSL_get_app_data(ssl_)); + SSL_set_app_data(ssl_, 0); + } + + ::BIO_free(ext_bio_); + ::SSL_free(ssl_); +} + +SSL* engine::native_handle() +{ + return ssl_; +} + +asio::error_code engine::set_verify_mode( + verify_mode v, asio::error_code& ec) +{ + ::SSL_set_verify(ssl_, v, ::SSL_get_verify_callback(ssl_)); + + ec = asio::error_code(); + return ec; +} + +asio::error_code engine::set_verify_depth( + int depth, asio::error_code& ec) +{ + ::SSL_set_verify_depth(ssl_, depth); + + ec = asio::error_code(); + return ec; +} + +asio::error_code engine::set_verify_callback( + verify_callback_base* callback, asio::error_code& ec) +{ + if (SSL_get_app_data(ssl_)) + delete static_cast(SSL_get_app_data(ssl_)); + + SSL_set_app_data(ssl_, callback); + + ::SSL_set_verify(ssl_, ::SSL_get_verify_mode(ssl_), + &engine::verify_callback_function); + + ec = asio::error_code(); + return ec; +} + +int engine::verify_callback_function(int preverified, X509_STORE_CTX* ctx) +{ + if (ctx) + { + if (SSL* ssl = static_cast( + ::X509_STORE_CTX_get_ex_data( + ctx, ::SSL_get_ex_data_X509_STORE_CTX_idx()))) + { + if (SSL_get_app_data(ssl)) + { + verify_callback_base* callback = + static_cast( + SSL_get_app_data(ssl)); + + verify_context verify_ctx(ctx); + return callback->call(preverified != 0, verify_ctx) ? 1 : 0; + } + } + } + + return 0; +} + +engine::want engine::handshake( + stream_base::handshake_type type, asio::error_code& ec) +{ + return perform((type == asio::ssl::stream_base::client) + ? &engine::do_connect : &engine::do_accept, 0, 0, ec, 0); +} + +engine::want engine::shutdown(asio::error_code& ec) +{ + return perform(&engine::do_shutdown, 0, 0, ec, 0); +} + +engine::want engine::write(const asio::const_buffer& data, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + if (data.size() == 0) + { + ec = asio::error_code(); + return engine::want_nothing; + } + + return perform(&engine::do_write, + const_cast(data.data()), + data.size(), ec, &bytes_transferred); +} + +engine::want engine::read(const asio::mutable_buffer& data, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + if (data.size() == 0) + { + ec = asio::error_code(); + return engine::want_nothing; + } + + return perform(&engine::do_read, data.data(), + data.size(), ec, &bytes_transferred); +} + +asio::mutable_buffer engine::get_output( + const asio::mutable_buffer& data) +{ + int length = ::BIO_read(ext_bio_, + data.data(), static_cast(data.size())); + + return asio::buffer(data, + length > 0 ? static_cast(length) : 0); +} + +asio::const_buffer engine::put_input( + const asio::const_buffer& data) +{ + int length = ::BIO_write(ext_bio_, + data.data(), static_cast(data.size())); + + return asio::buffer(data + + (length > 0 ? static_cast(length) : 0)); +} + +const asio::error_code& engine::map_error_code( + asio::error_code& ec) const +{ + // We only want to map the error::eof code. + if (ec != asio::error::eof) + return ec; + + // If there's data yet to be read, it's an error. + if (BIO_wpending(ext_bio_)) + { + ec = asio::ssl::error::stream_truncated; + return ec; + } + + // SSL v2 doesn't provide a protocol-level shutdown, so an eof on the + // underlying transport is passed through. +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_version(ssl_) == SSL2_VERSION) + return ec; +#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L) + + // Otherwise, the peer should have negotiated a proper shutdown. + if ((::SSL_get_shutdown(ssl_) & SSL_RECEIVED_SHUTDOWN) == 0) + { + ec = asio::ssl::error::stream_truncated; + } + + return ec; +} + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) +asio::detail::static_mutex& engine::accept_mutex() +{ + static asio::detail::static_mutex mutex = ASIO_STATIC_MUTEX_INIT; + return mutex; +} +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) + +engine::want engine::perform(int (engine::* op)(void*, std::size_t), + void* data, std::size_t length, asio::error_code& ec, + std::size_t* bytes_transferred) +{ + std::size_t pending_output_before = ::BIO_ctrl_pending(ext_bio_); + ::ERR_clear_error(); + int result = (this->*op)(data, length); + int ssl_error = ::SSL_get_error(ssl_, result); + int sys_error = static_cast(::ERR_get_error()); + std::size_t pending_output_after = ::BIO_ctrl_pending(ext_bio_); + + if (ssl_error == SSL_ERROR_SSL) + { + ec = asio::error_code(sys_error, + asio::error::get_ssl_category()); + return want_nothing; + } + + if (ssl_error == SSL_ERROR_SYSCALL) + { + ec = asio::error_code(sys_error, + asio::error::get_system_category()); + return want_nothing; + } + + if (result > 0 && bytes_transferred) + *bytes_transferred = static_cast(result); + + if (ssl_error == SSL_ERROR_WANT_WRITE) + { + ec = asio::error_code(); + return want_output_and_retry; + } + else if (pending_output_after > pending_output_before) + { + ec = asio::error_code(); + return result > 0 ? want_output : want_output_and_retry; + } + else if (ssl_error == SSL_ERROR_WANT_READ) + { + ec = asio::error_code(); + return want_input_and_retry; + } + else if (::SSL_get_shutdown(ssl_) & SSL_RECEIVED_SHUTDOWN) + { + ec = asio::error::eof; + return want_nothing; + } + else + { + ec = asio::error_code(); + return want_nothing; + } +} + +int engine::do_accept(void*, std::size_t) +{ +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + asio::detail::static_mutex::scoped_lock lock(accept_mutex()); +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) + return ::SSL_accept(ssl_); +} + +int engine::do_connect(void*, std::size_t) +{ + return ::SSL_connect(ssl_); +} + +int engine::do_shutdown(void*, std::size_t) +{ + int result = ::SSL_shutdown(ssl_); + if (result == 0) + result = ::SSL_shutdown(ssl_); + return result; +} + +int engine::do_read(void* data, std::size_t length) +{ + return ::SSL_read(ssl_, data, + length < INT_MAX ? static_cast(length) : INT_MAX); +} + +int engine::do_write(void* data, std::size_t length) +{ + return ::SSL_write(ssl_, data, + length < INT_MAX ? static_cast(length) : INT_MAX); +} + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_IMPL_ENGINE_IPP diff --git a/subprojects/asio/include/asio/ssl/detail/impl/openssl_init.ipp b/subprojects/asio/include/asio/ssl/detail/impl/openssl_init.ipp new file mode 100644 index 000000000..fb0fff949 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/impl/openssl_init.ipp @@ -0,0 +1,165 @@ +// +// ssl/detail/impl/openssl_init.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_IMPL_OPENSSL_INIT_IPP +#define ASIO_SSL_DETAIL_IMPL_OPENSSL_INIT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/tss_ptr.hpp" +#include "asio/ssl/detail/openssl_init.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class openssl_init_base::do_init +{ +public: + do_init() + { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + ::SSL_library_init(); + ::SSL_load_error_strings(); + ::OpenSSL_add_all_algorithms(); + + mutexes_.resize(::CRYPTO_num_locks()); + for (size_t i = 0; i < mutexes_.size(); ++i) + mutexes_[i].reset(new asio::detail::mutex); + ::CRYPTO_set_locking_callback(&do_init::openssl_locking_func); +#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L) +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + ::CRYPTO_set_id_callback(&do_init::openssl_id_func); +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) + +#if !defined(SSL_OP_NO_COMPRESSION) \ + && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + null_compression_methods_ = sk_SSL_COMP_new_null(); +#endif // !defined(SSL_OP_NO_COMPRESSION) + // && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + } + + ~do_init() + { +#if !defined(SSL_OP_NO_COMPRESSION) \ + && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + sk_SSL_COMP_free(null_compression_methods_); +#endif // !defined(SSL_OP_NO_COMPRESSION) + // && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + ::CRYPTO_set_id_callback(0); +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + ::CRYPTO_set_locking_callback(0); + ::ERR_free_strings(); + ::EVP_cleanup(); + ::CRYPTO_cleanup_all_ex_data(); +#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L) +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + ::ERR_remove_state(0); +#elif (OPENSSL_VERSION_NUMBER < 0x10100000L) + ::ERR_remove_thread_state(NULL); +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) \ + && (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + && !defined(SSL_OP_NO_COMPRESSION) + ::SSL_COMP_free_compression_methods(); +#endif // (OPENSSL_VERSION_NUMBER >= 0x10002000L) + // && (OPENSSL_VERSION_NUMBER < 0x10100000L) + // && !defined(SSL_OP_NO_COMPRESSION) +#if !defined(OPENSSL_IS_BORINGSSL) + ::CONF_modules_unload(1); +#endif // !defined(OPENSSL_IS_BORINGSSL) +#if !defined(OPENSSL_NO_ENGINE) \ + && (OPENSSL_VERSION_NUMBER < 0x10100000L) + ::ENGINE_cleanup(); +#endif // !defined(OPENSSL_NO_ENGINE) + // && (OPENSSL_VERSION_NUMBER < 0x10100000L) + } + +#if !defined(SSL_OP_NO_COMPRESSION) \ + && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + STACK_OF(SSL_COMP)* get_null_compression_methods() const + { + return null_compression_methods_; + } +#endif // !defined(SSL_OP_NO_COMPRESSION) + // && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + +private: +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + static unsigned long openssl_id_func() + { +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + return ::GetCurrentThreadId(); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + void* id = &errno; + ASIO_ASSERT(sizeof(unsigned long) >= sizeof(void*)); + return reinterpret_cast(id); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + } +#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + static void openssl_locking_func(int mode, int n, + const char* /*file*/, int /*line*/) + { + if (mode & CRYPTO_LOCK) + instance()->mutexes_[n]->lock(); + else + instance()->mutexes_[n]->unlock(); + } + + // Mutexes to be used in locking callbacks. + std::vector > mutexes_; +#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L) + +#if !defined(SSL_OP_NO_COMPRESSION) \ + && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + STACK_OF(SSL_COMP)* null_compression_methods_; +#endif // !defined(SSL_OP_NO_COMPRESSION) + // && (OPENSSL_VERSION_NUMBER >= 0x00908000L) +}; + +asio::detail::shared_ptr +openssl_init_base::instance() +{ + static asio::detail::shared_ptr init(new do_init); + return init; +} + +#if !defined(SSL_OP_NO_COMPRESSION) \ + && (OPENSSL_VERSION_NUMBER >= 0x00908000L) +STACK_OF(SSL_COMP)* openssl_init_base::get_null_compression_methods() +{ + return instance()->get_null_compression_methods(); +} +#endif // !defined(SSL_OP_NO_COMPRESSION) + // && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_IMPL_OPENSSL_INIT_IPP diff --git a/subprojects/asio/include/asio/ssl/detail/io.hpp b/subprojects/asio/include/asio/ssl/detail/io.hpp new file mode 100644 index 000000000..0b0e51a20 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/io.hpp @@ -0,0 +1,372 @@ +// +// ssl/detail/io.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_IO_HPP +#define ASIO_SSL_DETAIL_IO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/ssl/detail/engine.hpp" +#include "asio/ssl/detail/stream_core.hpp" +#include "asio/write.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +template +std::size_t io(Stream& next_layer, stream_core& core, + const Operation& op, asio::error_code& ec) +{ + std::size_t bytes_transferred = 0; + do switch (op(core.engine_, ec, bytes_transferred)) + { + case engine::want_input_and_retry: + + // If the input buffer is empty then we need to read some more data from + // the underlying transport. + if (core.input_.size() == 0) + core.input_ = asio::buffer(core.input_buffer_, + next_layer.read_some(core.input_buffer_, ec)); + + // Pass the new input data to the engine. + core.input_ = core.engine_.put_input(core.input_); + + // Try the operation again. + continue; + + case engine::want_output_and_retry: + + // Get output data from the engine and write it to the underlying + // transport. + asio::write(next_layer, + core.engine_.get_output(core.output_buffer_), ec); + + // Try the operation again. + continue; + + case engine::want_output: + + // Get output data from the engine and write it to the underlying + // transport. + asio::write(next_layer, + core.engine_.get_output(core.output_buffer_), ec); + + // Operation is complete. Return result to caller. + core.engine_.map_error_code(ec); + return bytes_transferred; + + default: + + // Operation is complete. Return result to caller. + core.engine_.map_error_code(ec); + return bytes_transferred; + + } while (!ec); + + // Operation failed. Return result to caller. + core.engine_.map_error_code(ec); + return 0; +} + +template +class io_op +{ +public: + io_op(Stream& next_layer, stream_core& core, + const Operation& op, Handler& handler) + : next_layer_(next_layer), + core_(core), + op_(op), + start_(0), + want_(engine::want_nothing), + bytes_transferred_(0), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + io_op(const io_op& other) + : next_layer_(other.next_layer_), + core_(other.core_), + op_(other.op_), + start_(other.start_), + want_(other.want_), + ec_(other.ec_), + bytes_transferred_(other.bytes_transferred_), + handler_(other.handler_) + { + } + + io_op(io_op&& other) + : next_layer_(other.next_layer_), + core_(other.core_), + op_(ASIO_MOVE_CAST(Operation)(other.op_)), + start_(other.start_), + want_(other.want_), + ec_(other.ec_), + bytes_transferred_(other.bytes_transferred_), + handler_(ASIO_MOVE_CAST(Handler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()(asio::error_code ec, + std::size_t bytes_transferred = ~std::size_t(0), int start = 0) + { + switch (start_ = start) + { + case 1: // Called after at least one async operation. + do + { + switch (want_ = op_(core_.engine_, ec_, bytes_transferred_)) + { + case engine::want_input_and_retry: + + // If the input buffer already has data in it we can pass it to the + // engine and then retry the operation immediately. + if (core_.input_.size() != 0) + { + core_.input_ = core_.engine_.put_input(core_.input_); + continue; + } + + // The engine wants more data to be read from input. However, we + // cannot allow more than one read operation at a time on the + // underlying transport. The pending_read_ timer's expiry is set to + // pos_infin if a read is in progress, and neg_infin otherwise. + if (core_.expiry(core_.pending_read_) == core_.neg_infin()) + { + // Prevent other read operations from being started. + core_.pending_read_.expires_at(core_.pos_infin()); + + // Start reading some data from the underlying transport. + next_layer_.async_read_some( + asio::buffer(core_.input_buffer_), + ASIO_MOVE_CAST(io_op)(*this)); + } + else + { + // Wait until the current read operation completes. + core_.pending_read_.async_wait(ASIO_MOVE_CAST(io_op)(*this)); + } + + // Yield control until asynchronous operation completes. Control + // resumes at the "default:" label below. + return; + + case engine::want_output_and_retry: + case engine::want_output: + + // The engine wants some data to be written to the output. However, we + // cannot allow more than one write operation at a time on the + // underlying transport. The pending_write_ timer's expiry is set to + // pos_infin if a write is in progress, and neg_infin otherwise. + if (core_.expiry(core_.pending_write_) == core_.neg_infin()) + { + // Prevent other write operations from being started. + core_.pending_write_.expires_at(core_.pos_infin()); + + // Start writing all the data to the underlying transport. + asio::async_write(next_layer_, + core_.engine_.get_output(core_.output_buffer_), + ASIO_MOVE_CAST(io_op)(*this)); + } + else + { + // Wait until the current write operation completes. + core_.pending_write_.async_wait(ASIO_MOVE_CAST(io_op)(*this)); + } + + // Yield control until asynchronous operation completes. Control + // resumes at the "default:" label below. + return; + + default: + + // The SSL operation is done and we can invoke the handler, but we + // have to keep in mind that this function might be being called from + // the async operation's initiating function. In this case we're not + // allowed to call the handler directly. Instead, issue a zero-sized + // read so the handler runs "as-if" posted using io_context::post(). + if (start) + { + next_layer_.async_read_some( + asio::buffer(core_.input_buffer_, 0), + ASIO_MOVE_CAST(io_op)(*this)); + + // Yield control until asynchronous operation completes. Control + // resumes at the "default:" label below. + return; + } + else + { + // Continue on to run handler directly. + break; + } + } + + default: + if (bytes_transferred == ~std::size_t(0)) + bytes_transferred = 0; // Timer cancellation, no data transferred. + else if (!ec_) + ec_ = ec; + + switch (want_) + { + case engine::want_input_and_retry: + + // Add received data to the engine's input. + core_.input_ = asio::buffer( + core_.input_buffer_, bytes_transferred); + core_.input_ = core_.engine_.put_input(core_.input_); + + // Release any waiting read operations. + core_.pending_read_.expires_at(core_.neg_infin()); + + // Try the operation again. + continue; + + case engine::want_output_and_retry: + + // Release any waiting write operations. + core_.pending_write_.expires_at(core_.neg_infin()); + + // Try the operation again. + continue; + + case engine::want_output: + + // Release any waiting write operations. + core_.pending_write_.expires_at(core_.neg_infin()); + + // Fall through to call handler. + + default: + + // Pass the result to the handler. + op_.call_handler(handler_, + core_.engine_.map_error_code(ec_), + ec_ ? 0 : bytes_transferred_); + + // Our work here is done. + return; + } + } while (!ec_); + + // Operation failed. Pass the result to the handler. + op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0); + } + } + +//private: + Stream& next_layer_; + stream_core& core_; + Operation op_; + int start_; + engine::want want_; + asio::error_code ec_; + std::size_t bytes_transferred_; + Handler handler_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + io_op* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + io_op* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + io_op* this_handler) +{ + return this_handler->start_ == 0 ? true + : asio_handler_cont_helpers::is_continuation(this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + io_op* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + io_op* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void async_io(Stream& next_layer, stream_core& core, + const Operation& op, Handler& handler) +{ + io_op( + next_layer, core, op, handler)( + asio::error_code(), 0, 1); +} + +} // namespace detail +} // namespace ssl + +template +struct associated_allocator< + ssl::detail::io_op, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const ssl::detail::io_op& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor< + ssl::detail::io_op, Executor> +{ + typedef typename associated_executor::type type; + + static type get(const ssl::detail::io_op& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_IO_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/openssl_init.hpp b/subprojects/asio/include/asio/ssl/detail/openssl_init.hpp new file mode 100644 index 000000000..c3e47278f --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/openssl_init.hpp @@ -0,0 +1,101 @@ +// +// ssl/detail/openssl_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_OPENSSL_INIT_HPP +#define ASIO_SSL_DETAIL_OPENSSL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class openssl_init_base + : private noncopyable +{ +protected: + // Class that performs the actual initialisation. + class do_init; + + // Helper function to manage a do_init singleton. The static instance of the + // openssl_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + ASIO_DECL static asio::detail::shared_ptr instance(); + +#if !defined(SSL_OP_NO_COMPRESSION) \ + && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + // Get an empty stack of compression methods, to be used when disabling + // compression. + ASIO_DECL static STACK_OF(SSL_COMP)* get_null_compression_methods(); +#endif // !defined(SSL_OP_NO_COMPRESSION) + // && (OPENSSL_VERSION_NUMBER >= 0x00908000L) +}; + +template +class openssl_init : private openssl_init_base +{ +public: + // Constructor. + openssl_init() + : ref_(instance()) + { + using namespace std; // For memmove. + + // Ensure openssl_init::instance_ is linked in. + openssl_init* tmp = &instance_; + memmove(&tmp, &tmp, sizeof(openssl_init*)); + } + + // Destructor. + ~openssl_init() + { + } + +#if !defined(SSL_OP_NO_COMPRESSION) \ + && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + using openssl_init_base::get_null_compression_methods; +#endif // !defined(SSL_OP_NO_COMPRESSION) + // && (OPENSSL_VERSION_NUMBER >= 0x00908000L) + +private: + // Instance to force initialisation of openssl at global scope. + static openssl_init instance_; + + // Reference to singleton do_init object to ensure that openssl does not get + // cleaned up until the last user has finished with it. + asio::detail::shared_ptr ref_; +}; + +template +openssl_init openssl_init::instance_; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/ssl/detail/impl/openssl_init.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_SSL_DETAIL_OPENSSL_INIT_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/openssl_types.hpp b/subprojects/asio/include/asio/ssl/detail/openssl_types.hpp new file mode 100644 index 000000000..a044af30d --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/openssl_types.hpp @@ -0,0 +1,30 @@ +// +// ssl/detail/openssl_types.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP +#define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/socket_types.hpp" +#include +#include +#if !defined(OPENSSL_NO_ENGINE) +# include +#endif // !defined(OPENSSL_NO_ENGINE) +#include +#include +#include +#include + +#endif // ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/password_callback.hpp b/subprojects/asio/include/asio/ssl/detail/password_callback.hpp new file mode 100644 index 000000000..9b1dbeeb8 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/password_callback.hpp @@ -0,0 +1,66 @@ +// +// ssl/detail/password_callback.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_PASSWORD_CALLBACK_HPP +#define ASIO_SSL_DETAIL_PASSWORD_CALLBACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include "asio/ssl/context_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class password_callback_base +{ +public: + virtual ~password_callback_base() + { + } + + virtual std::string call(std::size_t size, + context_base::password_purpose purpose) = 0; +}; + +template +class password_callback : public password_callback_base +{ +public: + explicit password_callback(PasswordCallback callback) + : callback_(callback) + { + } + + virtual std::string call(std::size_t size, + context_base::password_purpose purpose) + { + return callback_(size, purpose); + } + +private: + PasswordCallback callback_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_PASSWORD_CALLBACK_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/read_op.hpp b/subprojects/asio/include/asio/ssl/detail/read_op.hpp new file mode 100644 index 000000000..b0d6de266 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/read_op.hpp @@ -0,0 +1,67 @@ +// +// ssl/detail/read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_READ_OP_HPP +#define ASIO_SSL_DETAIL_READ_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/ssl/detail/engine.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +template +class read_op +{ +public: + read_op(const MutableBufferSequence& buffers) + : buffers_(buffers) + { + } + + engine::want operator()(engine& eng, + asio::error_code& ec, + std::size_t& bytes_transferred) const + { + asio::mutable_buffer buffer = + asio::detail::buffer_sequence_adapter::first(buffers_); + + return eng.read(buffer, ec, bytes_transferred); + } + + template + void call_handler(Handler& handler, + const asio::error_code& ec, + const std::size_t& bytes_transferred) const + { + handler(ec, bytes_transferred); + } + +private: + MutableBufferSequence buffers_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_READ_OP_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/shutdown_op.hpp b/subprojects/asio/include/asio/ssl/detail/shutdown_op.hpp new file mode 100644 index 000000000..d20b4309e --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/shutdown_op.hpp @@ -0,0 +1,54 @@ +// +// ssl/detail/shutdown_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_SHUTDOWN_OP_HPP +#define ASIO_SSL_DETAIL_SHUTDOWN_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/ssl/detail/engine.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class shutdown_op +{ +public: + engine::want operator()(engine& eng, + asio::error_code& ec, + std::size_t& bytes_transferred) const + { + bytes_transferred = 0; + return eng.shutdown(ec); + } + + template + void call_handler(Handler& handler, + const asio::error_code& ec, + const std::size_t&) const + { + handler(ec); + } +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_SHUTDOWN_OP_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/stream_core.hpp b/subprojects/asio/include/asio/ssl/detail/stream_core.hpp new file mode 100644 index 000000000..13fde74bf --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/stream_core.hpp @@ -0,0 +1,134 @@ +// +// ssl/detail/stream_core.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_STREAM_CORE_HPP +#define ASIO_SSL_DETAIL_STREAM_CORE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) +# include "asio/deadline_timer.hpp" +#else // defined(ASIO_HAS_BOOST_DATE_TIME) +# include "asio/steady_timer.hpp" +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) +#include "asio/ssl/detail/engine.hpp" +#include "asio/buffer.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +struct stream_core +{ + // According to the OpenSSL documentation, this is the buffer size that is + // sufficient to hold the largest possible TLS record. + enum { max_tls_record_size = 17 * 1024 }; + + stream_core(SSL_CTX* context, asio::io_context& io_context) + : engine_(context), + pending_read_(io_context), + pending_write_(io_context), + output_buffer_space_(max_tls_record_size), + output_buffer_(asio::buffer(output_buffer_space_)), + input_buffer_space_(max_tls_record_size), + input_buffer_(asio::buffer(input_buffer_space_)) + { + pending_read_.expires_at(neg_infin()); + pending_write_.expires_at(neg_infin()); + } + + ~stream_core() + { + } + + // The SSL engine. + engine engine_; + +#if defined(ASIO_HAS_BOOST_DATE_TIME) + // Timer used for storing queued read operations. + asio::deadline_timer pending_read_; + + // Timer used for storing queued write operations. + asio::deadline_timer pending_write_; + + // Helper function for obtaining a time value that always fires. + static asio::deadline_timer::time_type neg_infin() + { + return boost::posix_time::neg_infin; + } + + // Helper function for obtaining a time value that never fires. + static asio::deadline_timer::time_type pos_infin() + { + return boost::posix_time::pos_infin; + } + + // Helper function to get a timer's expiry time. + static asio::deadline_timer::time_type expiry( + const asio::deadline_timer& timer) + { + return timer.expires_at(); + } +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // Timer used for storing queued read operations. + asio::steady_timer pending_read_; + + // Timer used for storing queued write operations. + asio::steady_timer pending_write_; + + // Helper function for obtaining a time value that always fires. + static asio::steady_timer::time_point neg_infin() + { + return (asio::steady_timer::time_point::min)(); + } + + // Helper function for obtaining a time value that never fires. + static asio::steady_timer::time_point pos_infin() + { + return (asio::steady_timer::time_point::max)(); + } + + // Helper function to get a timer's expiry time. + static asio::steady_timer::time_point expiry( + const asio::steady_timer& timer) + { + return timer.expiry(); + } +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + + // Buffer space used to prepare output intended for the transport. + std::vector output_buffer_space_; + + // A buffer that may be used to prepare output intended for the transport. + const asio::mutable_buffer output_buffer_; + + // Buffer space used to read input intended for the engine. + std::vector input_buffer_space_; + + // A buffer that may be used to read input intended for the engine. + const asio::mutable_buffer input_buffer_; + + // The buffer pointing to the engine's unconsumed input. + asio::const_buffer input_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_STREAM_CORE_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/verify_callback.hpp b/subprojects/asio/include/asio/ssl/detail/verify_callback.hpp new file mode 100644 index 000000000..1c56a27d8 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/verify_callback.hpp @@ -0,0 +1,62 @@ +// +// ssl/detail/verify_callback.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_VERIFY_CALLBACK_HPP +#define ASIO_SSL_DETAIL_VERIFY_CALLBACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/ssl/verify_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class verify_callback_base +{ +public: + virtual ~verify_callback_base() + { + } + + virtual bool call(bool preverified, verify_context& ctx) = 0; +}; + +template +class verify_callback : public verify_callback_base +{ +public: + explicit verify_callback(VerifyCallback callback) + : callback_(callback) + { + } + + virtual bool call(bool preverified, verify_context& ctx) + { + return callback_(preverified, ctx); + } + +private: + VerifyCallback callback_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_VERIFY_CALLBACK_HPP diff --git a/subprojects/asio/include/asio/ssl/detail/write_op.hpp b/subprojects/asio/include/asio/ssl/detail/write_op.hpp new file mode 100644 index 000000000..1d341c09d --- /dev/null +++ b/subprojects/asio/include/asio/ssl/detail/write_op.hpp @@ -0,0 +1,67 @@ +// +// ssl/detail/write_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_WRITE_OP_HPP +#define ASIO_SSL_DETAIL_WRITE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/ssl/detail/engine.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +template +class write_op +{ +public: + write_op(const ConstBufferSequence& buffers) + : buffers_(buffers) + { + } + + engine::want operator()(engine& eng, + asio::error_code& ec, + std::size_t& bytes_transferred) const + { + asio::const_buffer buffer = + asio::detail::buffer_sequence_adapter::first(buffers_); + + return eng.write(buffer, ec, bytes_transferred); + } + + template + void call_handler(Handler& handler, + const asio::error_code& ec, + const std::size_t& bytes_transferred) const + { + handler(ec, bytes_transferred); + } + +private: + ConstBufferSequence buffers_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_WRITE_OP_HPP diff --git a/subprojects/asio/include/asio/ssl/error.hpp b/subprojects/asio/include/asio/ssl/error.hpp new file mode 100644 index 000000000..6165c5cf7 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/error.hpp @@ -0,0 +1,111 @@ +// +// ssl/error.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_ERROR_HPP +#define ASIO_SSL_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error_code.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace error { + +enum ssl_errors +{ + // Error numbers are those produced by openssl. +}; + +extern ASIO_DECL +const asio::error_category& get_ssl_category(); + +static const asio::error_category& + ssl_category ASIO_UNUSED_VARIABLE + = asio::error::get_ssl_category(); + +} // namespace error +namespace ssl { +namespace error { + +enum stream_errors +{ +#if defined(GENERATING_DOCUMENTATION) + /// The underlying stream closed before the ssl stream gracefully shut down. + stream_truncated +#elif (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_IS_BORINGSSL) + stream_truncated = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ) +#else + stream_truncated = 1 +#endif +}; + +extern ASIO_DECL +const asio::error_category& get_stream_category(); + +static const asio::error_category& + stream_category ASIO_UNUSED_VARIABLE + = asio::ssl::error::get_stream_category(); + +} // namespace error +} // namespace ssl +} // namespace asio + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) +namespace std { + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +} // namespace std +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +namespace asio { +namespace error { + +inline asio::error_code make_error_code(ssl_errors e) +{ + return asio::error_code( + static_cast(e), get_ssl_category()); +} + +} // namespace error +namespace ssl { +namespace error { + +inline asio::error_code make_error_code(stream_errors e) +{ + return asio::error_code( + static_cast(e), get_stream_category()); +} + +} // namespace error +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/ssl/impl/error.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_SSL_ERROR_HPP diff --git a/subprojects/asio/include/asio/ssl/impl/context.hpp b/subprojects/asio/include/asio/ssl/impl/context.hpp new file mode 100644 index 000000000..40199c143 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/impl/context.hpp @@ -0,0 +1,67 @@ +// +// ssl/impl/context.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_IMPL_CONTEXT_HPP +#define ASIO_SSL_IMPL_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +template +void context::set_verify_callback(VerifyCallback callback) +{ + asio::error_code ec; + this->set_verify_callback(callback, ec); + asio::detail::throw_error(ec, "set_verify_callback"); +} + +template +ASIO_SYNC_OP_VOID context::set_verify_callback( + VerifyCallback callback, asio::error_code& ec) +{ + do_set_verify_callback( + new detail::verify_callback(callback), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +template +void context::set_password_callback(PasswordCallback callback) +{ + asio::error_code ec; + this->set_password_callback(callback, ec); + asio::detail::throw_error(ec, "set_password_callback"); +} + +template +ASIO_SYNC_OP_VOID context::set_password_callback( + PasswordCallback callback, asio::error_code& ec) +{ + do_set_password_callback( + new detail::password_callback(callback), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_IMPL_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/ssl/impl/context.ipp b/subprojects/asio/include/asio/ssl/impl/context.ipp new file mode 100644 index 000000000..3bc3f295c --- /dev/null +++ b/subprojects/asio/include/asio/ssl/impl/context.ipp @@ -0,0 +1,1204 @@ +// +// ssl/impl/context.ipp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_IMPL_CONTEXT_IPP +#define ASIO_SSL_IMPL_CONTEXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/ssl/context.hpp" +#include "asio/ssl/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +struct context::bio_cleanup +{ + BIO* p; + ~bio_cleanup() { if (p) ::BIO_free(p); } +}; + +struct context::x509_cleanup +{ + X509* p; + ~x509_cleanup() { if (p) ::X509_free(p); } +}; + +struct context::evp_pkey_cleanup +{ + EVP_PKEY* p; + ~evp_pkey_cleanup() { if (p) ::EVP_PKEY_free(p); } +}; + +struct context::rsa_cleanup +{ + RSA* p; + ~rsa_cleanup() { if (p) ::RSA_free(p); } +}; + +struct context::dh_cleanup +{ + DH* p; + ~dh_cleanup() { if (p) ::DH_free(p); } +}; + +context::context(context::method m) + : handle_(0) +{ + ::ERR_clear_error(); + + switch (m) + { + // SSL v2. +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) || defined(OPENSSL_NO_SSL2) + case context::sslv2: + case context::sslv2_client: + case context::sslv2_server: + asio::detail::throw_error( + asio::error::invalid_argument, "context"); + break; +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) || defined(OPENSSL_NO_SSL2) + case context::sslv2: + handle_ = ::SSL_CTX_new(::SSLv2_method()); + break; + case context::sslv2_client: + handle_ = ::SSL_CTX_new(::SSLv2_client_method()); + break; + case context::sslv2_server: + handle_ = ::SSL_CTX_new(::SSLv2_server_method()); + break; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) || defined(OPENSSL_NO_SSL2) + + // SSL v3. +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + case context::sslv3: + handle_ = ::SSL_CTX_new(::TLS_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, SSL3_VERSION); + SSL_CTX_set_max_proto_version(handle_, SSL3_VERSION); + } + break; + case context::sslv3_client: + handle_ = ::SSL_CTX_new(::TLS_client_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, SSL3_VERSION); + SSL_CTX_set_max_proto_version(handle_, SSL3_VERSION); + } + break; + case context::sslv3_server: + handle_ = ::SSL_CTX_new(::TLS_server_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, SSL3_VERSION); + SSL_CTX_set_max_proto_version(handle_, SSL3_VERSION); + } + break; +#elif defined(OPENSSL_NO_SSL3) + case context::sslv3: + case context::sslv3_client: + case context::sslv3_server: + asio::detail::throw_error( + asio::error::invalid_argument, "context"); + break; +#else // defined(OPENSSL_NO_SSL3) + case context::sslv3: + handle_ = ::SSL_CTX_new(::SSLv3_method()); + break; + case context::sslv3_client: + handle_ = ::SSL_CTX_new(::SSLv3_client_method()); + break; + case context::sslv3_server: + handle_ = ::SSL_CTX_new(::SSLv3_server_method()); + break; +#endif // defined(OPENSSL_NO_SSL3) + + // TLS v1.0. +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + case context::tlsv1: + handle_ = ::SSL_CTX_new(::TLS_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_VERSION); + } + break; + case context::tlsv1_client: + handle_ = ::SSL_CTX_new(::TLS_client_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_VERSION); + } + break; + case context::tlsv1_server: + handle_ = ::SSL_CTX_new(::TLS_server_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_VERSION); + } + break; +#elif defined(SSL_TXT_TLSV1) + case context::tlsv1: + handle_ = ::SSL_CTX_new(::TLSv1_method()); + break; + case context::tlsv1_client: + handle_ = ::SSL_CTX_new(::TLSv1_client_method()); + break; + case context::tlsv1_server: + handle_ = ::SSL_CTX_new(::TLSv1_server_method()); + break; +#else // defined(SSL_TXT_TLSV1) + case context::tlsv1: + case context::tlsv1_client: + case context::tlsv1_server: + asio::detail::throw_error( + asio::error::invalid_argument, "context"); + break; +#endif // defined(SSL_TXT_TLSV1) + + // TLS v1.1. +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + case context::tlsv11: + handle_ = ::SSL_CTX_new(::TLS_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_1_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_1_VERSION); + } + break; + case context::tlsv11_client: + handle_ = ::SSL_CTX_new(::TLS_client_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_1_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_1_VERSION); + } + break; + case context::tlsv11_server: + handle_ = ::SSL_CTX_new(::TLS_server_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_1_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_1_VERSION); + } + break; +#elif defined(SSL_TXT_TLSV1_1) + case context::tlsv11: + handle_ = ::SSL_CTX_new(::TLSv1_1_method()); + break; + case context::tlsv11_client: + handle_ = ::SSL_CTX_new(::TLSv1_1_client_method()); + break; + case context::tlsv11_server: + handle_ = ::SSL_CTX_new(::TLSv1_1_server_method()); + break; +#else // defined(SSL_TXT_TLSV1_1) + case context::tlsv11: + case context::tlsv11_client: + case context::tlsv11_server: + asio::detail::throw_error( + asio::error::invalid_argument, "context"); + break; +#endif // defined(SSL_TXT_TLSV1_1) + + // TLS v1.2. +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + case context::tlsv12: + handle_ = ::SSL_CTX_new(::TLS_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_2_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_2_VERSION); + } + break; + case context::tlsv12_client: + handle_ = ::SSL_CTX_new(::TLS_client_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_2_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_2_VERSION); + } + break; + case context::tlsv12_server: + handle_ = ::SSL_CTX_new(::TLS_server_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_2_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_2_VERSION); + } + break; +#elif defined(SSL_TXT_TLSV1_2) + case context::tlsv12: + handle_ = ::SSL_CTX_new(::TLSv1_2_method()); + break; + case context::tlsv12_client: + handle_ = ::SSL_CTX_new(::TLSv1_2_client_method()); + break; + case context::tlsv12_server: + handle_ = ::SSL_CTX_new(::TLSv1_2_server_method()); + break; +#else // defined(SSL_TXT_TLSV1_2) + case context::tlsv12: + case context::tlsv12_client: + case context::tlsv12_server: + asio::detail::throw_error( + asio::error::invalid_argument, "context"); + break; +#endif // defined(SSL_TXT_TLSV1_2) + + // TLS v1.3. +#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) \ + && !defined(LIBRESSL_VERSION_NUMBER) + case context::tlsv13: + handle_ = ::SSL_CTX_new(::TLS_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_3_VERSION); + } + break; + case context::tlsv13_client: + handle_ = ::SSL_CTX_new(::TLS_client_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_3_VERSION); + } + break; + case context::tlsv13_server: + handle_ = ::SSL_CTX_new(::TLS_server_method()); + if (handle_) + { + SSL_CTX_set_min_proto_version(handle_, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(handle_, TLS1_3_VERSION); + } + break; +#else // (OPENSSL_VERSION_NUMBER >= 0x10101000L) + // && !defined(LIBRESSL_VERSION_NUMBER) + case context::tlsv13: + case context::tlsv13_client: + case context::tlsv13_server: + asio::detail::throw_error( + asio::error::invalid_argument, "context"); + break; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10101000L) + // && !defined(LIBRESSL_VERSION_NUMBER) + + // Any supported SSL/TLS version. + case context::sslv23: + handle_ = ::SSL_CTX_new(::SSLv23_method()); + break; + case context::sslv23_client: + handle_ = ::SSL_CTX_new(::SSLv23_client_method()); + break; + case context::sslv23_server: + handle_ = ::SSL_CTX_new(::SSLv23_server_method()); + break; + + // Any supported TLS version. +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + case context::tls: + handle_ = ::SSL_CTX_new(::TLS_method()); + if (handle_) + SSL_CTX_set_min_proto_version(handle_, TLS1_VERSION); + break; + case context::tls_client: + handle_ = ::SSL_CTX_new(::TLS_client_method()); + if (handle_) + SSL_CTX_set_min_proto_version(handle_, TLS1_VERSION); + break; + case context::tls_server: + handle_ = ::SSL_CTX_new(::TLS_server_method()); + if (handle_) + SSL_CTX_set_min_proto_version(handle_, TLS1_VERSION); + break; +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + case context::tls: + handle_ = ::SSL_CTX_new(::SSLv23_method()); + if (handle_) + SSL_CTX_set_options(handle_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + break; + case context::tls_client: + handle_ = ::SSL_CTX_new(::SSLv23_client_method()); + if (handle_) + SSL_CTX_set_options(handle_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + break; + case context::tls_server: + handle_ = ::SSL_CTX_new(::SSLv23_server_method()); + if (handle_) + SSL_CTX_set_options(handle_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + break; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + + default: + handle_ = ::SSL_CTX_new(0); + break; + } + + if (handle_ == 0) + { + asio::error_code ec( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + asio::detail::throw_error(ec, "context"); + } + + set_options(no_compression); +} + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) +context::context(context&& other) +{ + handle_ = other.handle_; + other.handle_ = 0; +} + +context& context::operator=(context&& other) +{ + context tmp(ASIO_MOVE_CAST(context)(*this)); + handle_ = other.handle_; + other.handle_ = 0; + return *this; +} +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +context::~context() +{ + if (handle_) + { +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + void* cb_userdata = ::SSL_CTX_get_default_passwd_cb_userdata(handle_); +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + void* cb_userdata = handle_->default_passwd_callback_userdata; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + if (cb_userdata) + { + detail::password_callback_base* callback = + static_cast( + cb_userdata); + delete callback; +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + ::SSL_CTX_set_default_passwd_cb_userdata(handle_, 0); +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + handle_->default_passwd_callback_userdata = 0; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + } + + if (SSL_CTX_get_app_data(handle_)) + { + detail::verify_callback_base* callback = + static_cast( + SSL_CTX_get_app_data(handle_)); + delete callback; + SSL_CTX_set_app_data(handle_, 0); + } + + ::SSL_CTX_free(handle_); + } +} + +context::native_handle_type context::native_handle() +{ + return handle_; +} + +void context::clear_options(context::options o) +{ + asio::error_code ec; + clear_options(o, ec); + asio::detail::throw_error(ec, "clear_options"); +} + +ASIO_SYNC_OP_VOID context::clear_options( + context::options o, asio::error_code& ec) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x009080DFL) \ + && (OPENSSL_VERSION_NUMBER != 0x00909000L) +# if !defined(SSL_OP_NO_COMPRESSION) + if ((o & context::no_compression) != 0) + { +# if (OPENSSL_VERSION_NUMBER >= 0x00908000L) + handle_->comp_methods = SSL_COMP_get_compression_methods(); +# endif // (OPENSSL_VERSION_NUMBER >= 0x00908000L) + o ^= context::no_compression; + } +# endif // !defined(SSL_OP_NO_COMPRESSION) + + ::SSL_CTX_clear_options(handle_, o); + + ec = asio::error_code(); +#else // (OPENSSL_VERSION_NUMBER >= 0x009080DFL) + // && (OPENSSL_VERSION_NUMBER != 0x00909000L) + (void)o; + ec = asio::error::operation_not_supported; +#endif // (OPENSSL_VERSION_NUMBER >= 0x009080DFL) + // && (OPENSSL_VERSION_NUMBER != 0x00909000L) + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::set_options(context::options o) +{ + asio::error_code ec; + set_options(o, ec); + asio::detail::throw_error(ec, "set_options"); +} + +ASIO_SYNC_OP_VOID context::set_options( + context::options o, asio::error_code& ec) +{ +#if !defined(SSL_OP_NO_COMPRESSION) + if ((o & context::no_compression) != 0) + { +#if (OPENSSL_VERSION_NUMBER >= 0x00908000L) + handle_->comp_methods = + asio::ssl::detail::openssl_init<>::get_null_compression_methods(); +#endif // (OPENSSL_VERSION_NUMBER >= 0x00908000L) + o ^= context::no_compression; + } +#endif // !defined(SSL_OP_NO_COMPRESSION) + + ::SSL_CTX_set_options(handle_, o); + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::set_verify_mode(verify_mode v) +{ + asio::error_code ec; + set_verify_mode(v, ec); + asio::detail::throw_error(ec, "set_verify_mode"); +} + +ASIO_SYNC_OP_VOID context::set_verify_mode( + verify_mode v, asio::error_code& ec) +{ + ::SSL_CTX_set_verify(handle_, v, ::SSL_CTX_get_verify_callback(handle_)); + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::set_verify_depth(int depth) +{ + asio::error_code ec; + set_verify_depth(depth, ec); + asio::detail::throw_error(ec, "set_verify_depth"); +} + +ASIO_SYNC_OP_VOID context::set_verify_depth( + int depth, asio::error_code& ec) +{ + ::SSL_CTX_set_verify_depth(handle_, depth); + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::load_verify_file(const std::string& filename) +{ + asio::error_code ec; + load_verify_file(filename, ec); + asio::detail::throw_error(ec, "load_verify_file"); +} + +ASIO_SYNC_OP_VOID context::load_verify_file( + const std::string& filename, asio::error_code& ec) +{ + ::ERR_clear_error(); + + if (::SSL_CTX_load_verify_locations(handle_, filename.c_str(), 0) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::add_certificate_authority(const const_buffer& ca) +{ + asio::error_code ec; + add_certificate_authority(ca, ec); + asio::detail::throw_error(ec, "add_certificate_authority"); +} + +ASIO_SYNC_OP_VOID context::add_certificate_authority( + const const_buffer& ca, asio::error_code& ec) +{ + ::ERR_clear_error(); + + bio_cleanup bio = { make_buffer_bio(ca) }; + if (bio.p) + { + if (X509_STORE* store = ::SSL_CTX_get_cert_store(handle_)) + { + for (;;) + { + x509_cleanup cert = { ::PEM_read_bio_X509(bio.p, 0, 0, 0) }; + if (!cert.p) + break; + + if (::X509_STORE_add_cert(store, cert.p) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + } + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::set_default_verify_paths() +{ + asio::error_code ec; + set_default_verify_paths(ec); + asio::detail::throw_error(ec, "set_default_verify_paths"); +} + +ASIO_SYNC_OP_VOID context::set_default_verify_paths( + asio::error_code& ec) +{ + ::ERR_clear_error(); + + if (::SSL_CTX_set_default_verify_paths(handle_) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::add_verify_path(const std::string& path) +{ + asio::error_code ec; + add_verify_path(path, ec); + asio::detail::throw_error(ec, "add_verify_path"); +} + +ASIO_SYNC_OP_VOID context::add_verify_path( + const std::string& path, asio::error_code& ec) +{ + ::ERR_clear_error(); + + if (::SSL_CTX_load_verify_locations(handle_, 0, path.c_str()) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_certificate( + const const_buffer& certificate, file_format format) +{ + asio::error_code ec; + use_certificate(certificate, format, ec); + asio::detail::throw_error(ec, "use_certificate"); +} + +ASIO_SYNC_OP_VOID context::use_certificate( + const const_buffer& certificate, file_format format, + asio::error_code& ec) +{ + ::ERR_clear_error(); + + if (format == context_base::asn1) + { + if (::SSL_CTX_use_certificate_ASN1(handle_, + static_cast(certificate.size()), + static_cast(certificate.data())) == 1) + { + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + else if (format == context_base::pem) + { + bio_cleanup bio = { make_buffer_bio(certificate) }; + if (bio.p) + { + x509_cleanup cert = { ::PEM_read_bio_X509(bio.p, 0, 0, 0) }; + if (cert.p) + { + if (::SSL_CTX_use_certificate(handle_, cert.p) == 1) + { + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + } + } + else + { + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_certificate_file( + const std::string& filename, file_format format) +{ + asio::error_code ec; + use_certificate_file(filename, format, ec); + asio::detail::throw_error(ec, "use_certificate_file"); +} + +ASIO_SYNC_OP_VOID context::use_certificate_file( + const std::string& filename, file_format format, + asio::error_code& ec) +{ + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + ::ERR_clear_error(); + + if (::SSL_CTX_use_certificate_file(handle_, filename.c_str(), file_type) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_certificate_chain(const const_buffer& chain) +{ + asio::error_code ec; + use_certificate_chain(chain, ec); + asio::detail::throw_error(ec, "use_certificate_chain"); +} + +ASIO_SYNC_OP_VOID context::use_certificate_chain( + const const_buffer& chain, asio::error_code& ec) +{ + ::ERR_clear_error(); + + bio_cleanup bio = { make_buffer_bio(chain) }; + if (bio.p) + { +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + pem_password_cb* callback = ::SSL_CTX_get_default_passwd_cb(handle_); + void* cb_userdata = ::SSL_CTX_get_default_passwd_cb_userdata(handle_); +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + pem_password_cb* callback = handle_->default_passwd_callback; + void* cb_userdata = handle_->default_passwd_callback_userdata; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + x509_cleanup cert = { + ::PEM_read_bio_X509_AUX(bio.p, 0, + callback, + cb_userdata) }; + if (!cert.p) + { + ec = asio::error_code(ERR_R_PEM_LIB, + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + int result = ::SSL_CTX_use_certificate(handle_, cert.p); + if (result == 0 || ::ERR_peek_error() != 0) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && !defined(LIBRESSL_VERSION_NUMBER) + ::SSL_CTX_clear_chain_certs(handle_); +#else + if (handle_->extra_certs) + { + ::sk_X509_pop_free(handle_->extra_certs, X509_free); + handle_->extra_certs = 0; + } +#endif // (OPENSSL_VERSION_NUMBER >= 0x10002000L) + + while (X509* cacert = ::PEM_read_bio_X509(bio.p, 0, + callback, + cb_userdata)) + { + if (!::SSL_CTX_add_extra_chain_cert(handle_, cacert)) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + result = ::ERR_peek_last_error(); + if ((ERR_GET_LIB(result) == ERR_LIB_PEM) + && (ERR_GET_REASON(result) == PEM_R_NO_START_LINE)) + { + ::ERR_clear_error(); + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_certificate_chain_file(const std::string& filename) +{ + asio::error_code ec; + use_certificate_chain_file(filename, ec); + asio::detail::throw_error(ec, "use_certificate_chain_file"); +} + +ASIO_SYNC_OP_VOID context::use_certificate_chain_file( + const std::string& filename, asio::error_code& ec) +{ + ::ERR_clear_error(); + + if (::SSL_CTX_use_certificate_chain_file(handle_, filename.c_str()) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_private_key( + const const_buffer& private_key, context::file_format format) +{ + asio::error_code ec; + use_private_key(private_key, format, ec); + asio::detail::throw_error(ec, "use_private_key"); +} + +ASIO_SYNC_OP_VOID context::use_private_key( + const const_buffer& private_key, context::file_format format, + asio::error_code& ec) +{ + ::ERR_clear_error(); + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + pem_password_cb* callback = ::SSL_CTX_get_default_passwd_cb(handle_); + void* cb_userdata = ::SSL_CTX_get_default_passwd_cb_userdata(handle_); +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + pem_password_cb* callback = handle_->default_passwd_callback; + void* cb_userdata = handle_->default_passwd_callback_userdata; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + + bio_cleanup bio = { make_buffer_bio(private_key) }; + if (bio.p) + { + evp_pkey_cleanup evp_private_key = { 0 }; + switch (format) + { + case context_base::asn1: + evp_private_key.p = ::d2i_PrivateKey_bio(bio.p, 0); + break; + case context_base::pem: + evp_private_key.p = ::PEM_read_bio_PrivateKey( + bio.p, 0, callback, + cb_userdata); + break; + default: + { + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + if (evp_private_key.p) + { + if (::SSL_CTX_use_PrivateKey(handle_, evp_private_key.p) == 1) + { + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_private_key_file( + const std::string& filename, context::file_format format) +{ + asio::error_code ec; + use_private_key_file(filename, format, ec); + asio::detail::throw_error(ec, "use_private_key_file"); +} + +void context::use_rsa_private_key( + const const_buffer& private_key, context::file_format format) +{ + asio::error_code ec; + use_rsa_private_key(private_key, format, ec); + asio::detail::throw_error(ec, "use_rsa_private_key"); +} + +ASIO_SYNC_OP_VOID context::use_rsa_private_key( + const const_buffer& private_key, context::file_format format, + asio::error_code& ec) +{ + ::ERR_clear_error(); + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + pem_password_cb* callback = ::SSL_CTX_get_default_passwd_cb(handle_); + void* cb_userdata = ::SSL_CTX_get_default_passwd_cb_userdata(handle_); +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + pem_password_cb* callback = handle_->default_passwd_callback; + void* cb_userdata = handle_->default_passwd_callback_userdata; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + + bio_cleanup bio = { make_buffer_bio(private_key) }; + if (bio.p) + { + rsa_cleanup rsa_private_key = { 0 }; + switch (format) + { + case context_base::asn1: + rsa_private_key.p = ::d2i_RSAPrivateKey_bio(bio.p, 0); + break; + case context_base::pem: + rsa_private_key.p = ::PEM_read_bio_RSAPrivateKey( + bio.p, 0, callback, + cb_userdata); + break; + default: + { + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + if (rsa_private_key.p) + { + if (::SSL_CTX_use_RSAPrivateKey(handle_, rsa_private_key.p) == 1) + { + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID context::use_private_key_file( + const std::string& filename, context::file_format format, + asio::error_code& ec) +{ + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + ::ERR_clear_error(); + + if (::SSL_CTX_use_PrivateKey_file(handle_, filename.c_str(), file_type) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_rsa_private_key_file( + const std::string& filename, context::file_format format) +{ + asio::error_code ec; + use_rsa_private_key_file(filename, format, ec); + asio::detail::throw_error(ec, "use_rsa_private_key_file"); +} + +ASIO_SYNC_OP_VOID context::use_rsa_private_key_file( + const std::string& filename, context::file_format format, + asio::error_code& ec) +{ + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + ::ERR_clear_error(); + + if (::SSL_CTX_use_RSAPrivateKey_file( + handle_, filename.c_str(), file_type) != 1) + { + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_tmp_dh(const const_buffer& dh) +{ + asio::error_code ec; + use_tmp_dh(dh, ec); + asio::detail::throw_error(ec, "use_tmp_dh"); +} + +ASIO_SYNC_OP_VOID context::use_tmp_dh( + const const_buffer& dh, asio::error_code& ec) +{ + ::ERR_clear_error(); + + bio_cleanup bio = { make_buffer_bio(dh) }; + if (bio.p) + { + return do_use_tmp_dh(bio.p, ec); + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +void context::use_tmp_dh_file(const std::string& filename) +{ + asio::error_code ec; + use_tmp_dh_file(filename, ec); + asio::detail::throw_error(ec, "use_tmp_dh_file"); +} + +ASIO_SYNC_OP_VOID context::use_tmp_dh_file( + const std::string& filename, asio::error_code& ec) +{ + ::ERR_clear_error(); + + bio_cleanup bio = { ::BIO_new_file(filename.c_str(), "r") }; + if (bio.p) + { + return do_use_tmp_dh(bio.p, ec); + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID context::do_use_tmp_dh( + BIO* bio, asio::error_code& ec) +{ + ::ERR_clear_error(); + + dh_cleanup dh = { ::PEM_read_bio_DHparams(bio, 0, 0, 0) }; + if (dh.p) + { + if (::SSL_CTX_set_tmp_dh(handle_, dh.p) == 1) + { + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +ASIO_SYNC_OP_VOID context::do_set_verify_callback( + detail::verify_callback_base* callback, asio::error_code& ec) +{ + if (SSL_CTX_get_app_data(handle_)) + { + delete static_cast( + SSL_CTX_get_app_data(handle_)); + } + + SSL_CTX_set_app_data(handle_, callback); + + ::SSL_CTX_set_verify(handle_, + ::SSL_CTX_get_verify_mode(handle_), + &context::verify_callback_function); + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +int context::verify_callback_function(int preverified, X509_STORE_CTX* ctx) +{ + if (ctx) + { + if (SSL* ssl = static_cast( + ::X509_STORE_CTX_get_ex_data( + ctx, ::SSL_get_ex_data_X509_STORE_CTX_idx()))) + { + if (SSL_CTX* handle = ::SSL_get_SSL_CTX(ssl)) + { + if (SSL_CTX_get_app_data(handle)) + { + detail::verify_callback_base* callback = + static_cast( + SSL_CTX_get_app_data(handle)); + + verify_context verify_ctx(ctx); + return callback->call(preverified != 0, verify_ctx) ? 1 : 0; + } + } + } + } + + return 0; +} + +ASIO_SYNC_OP_VOID context::do_set_password_callback( + detail::password_callback_base* callback, asio::error_code& ec) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + void* old_callback = ::SSL_CTX_get_default_passwd_cb_userdata(handle_); + ::SSL_CTX_set_default_passwd_cb_userdata(handle_, callback); +#else // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + void* old_callback = handle_->default_passwd_callback_userdata; + handle_->default_passwd_callback_userdata = callback; +#endif // (OPENSSL_VERSION_NUMBER >= 0x10100000L) + + if (old_callback) + delete static_cast( + old_callback); + + SSL_CTX_set_default_passwd_cb(handle_, &context::password_callback_function); + + ec = asio::error_code(); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + +int context::password_callback_function( + char* buf, int size, int purpose, void* data) +{ + using namespace std; // For strncat and strlen. + + if (data) + { + detail::password_callback_base* callback = + static_cast(data); + + std::string passwd = callback->call(static_cast(size), + purpose ? context_base::for_writing : context_base::for_reading); + +#if defined(ASIO_HAS_SECURE_RTL) + strcpy_s(buf, size, passwd.c_str()); +#else // defined(ASIO_HAS_SECURE_RTL) + *buf = '\0'; + if (size > 0) + strncat(buf, passwd.c_str(), size - 1); +#endif // defined(ASIO_HAS_SECURE_RTL) + + return static_cast(strlen(buf)); + } + + return 0; +} + +BIO* context::make_buffer_bio(const const_buffer& b) +{ + return ::BIO_new_mem_buf( + const_cast(b.data()), + static_cast(b.size())); +} + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_IMPL_CONTEXT_IPP diff --git a/subprojects/asio/include/asio/ssl/impl/error.ipp b/subprojects/asio/include/asio/ssl/impl/error.ipp new file mode 100644 index 000000000..98e8c91b4 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/impl/error.ipp @@ -0,0 +1,100 @@ +// +// ssl/impl/error.ipp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_IMPL_ERROR_IPP +#define ASIO_SSL_IMPL_ERROR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ssl/error.hpp" +#include "asio/ssl/detail/openssl_init.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace error { +namespace detail { + +class ssl_category : public asio::error_category +{ +public: + const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT + { + return "asio.ssl"; + } + + std::string message(int value) const + { + const char* s = ::ERR_reason_error_string(value); + return s ? s : "asio.ssl error"; + } +}; + +} // namespace detail + +const asio::error_category& get_ssl_category() +{ + static detail::ssl_category instance; + return instance; +} + +} // namespace error +namespace ssl { +namespace error { + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_IS_BORINGSSL) + +const asio::error_category& get_stream_category() +{ + return asio::error::get_ssl_category(); +} + +#else + +namespace detail { + +class stream_category : public asio::error_category +{ +public: + const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT + { + return "asio.ssl.stream"; + } + + std::string message(int value) const + { + switch (value) + { + case stream_truncated: return "stream truncated"; + default: return "asio.ssl.stream error"; + } + } +}; + +} // namespace detail + +const asio::error_category& get_stream_category() +{ + static detail::stream_category instance; + return instance; +} + +#endif + +} // namespace error +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_IMPL_ERROR_IPP diff --git a/subprojects/asio/include/asio/ssl/impl/rfc2818_verification.ipp b/subprojects/asio/include/asio/ssl/impl/rfc2818_verification.ipp new file mode 100644 index 000000000..577e4a12e --- /dev/null +++ b/subprojects/asio/include/asio/ssl/impl/rfc2818_verification.ipp @@ -0,0 +1,160 @@ +// +// ssl/impl/rfc2818_verification.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_IMPL_RFC2818_VERIFICATION_IPP +#define ASIO_SSL_IMPL_RFC2818_VERIFICATION_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include "asio/ip/address.hpp" +#include "asio/ssl/rfc2818_verification.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +bool rfc2818_verification::operator()( + bool preverified, verify_context& ctx) const +{ + using namespace std; // For memcmp. + + // Don't bother looking at certificates that have failed pre-verification. + if (!preverified) + return false; + + // We're only interested in checking the certificate at the end of the chain. + int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); + if (depth > 0) + return true; + + // Try converting the host name to an address. If it is an address then we + // need to look for an IP address in the certificate rather than a host name. + asio::error_code ec; + ip::address address = ip::make_address(host_, ec); + bool is_address = !ec; + + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + + // Go through the alternate names in the certificate looking for matching DNS + // or IP address entries. + GENERAL_NAMES* gens = static_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0)); + for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) + { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type == GEN_DNS && !is_address) + { + ASN1_IA5STRING* domain = gen->d.dNSName; + if (domain->type == V_ASN1_IA5STRING && domain->data && domain->length) + { + const char* pattern = reinterpret_cast(domain->data); + std::size_t pattern_length = domain->length; + if (match_pattern(pattern, pattern_length, host_.c_str())) + { + GENERAL_NAMES_free(gens); + return true; + } + } + } + else if (gen->type == GEN_IPADD && is_address) + { + ASN1_OCTET_STRING* ip_address = gen->d.iPAddress; + if (ip_address->type == V_ASN1_OCTET_STRING && ip_address->data) + { + if (address.is_v4() && ip_address->length == 4) + { + ip::address_v4::bytes_type bytes = address.to_v4().to_bytes(); + if (memcmp(bytes.data(), ip_address->data, 4) == 0) + { + GENERAL_NAMES_free(gens); + return true; + } + } + else if (address.is_v6() && ip_address->length == 16) + { + ip::address_v6::bytes_type bytes = address.to_v6().to_bytes(); + if (memcmp(bytes.data(), ip_address->data, 16) == 0) + { + GENERAL_NAMES_free(gens); + return true; + } + } + } + } + } + GENERAL_NAMES_free(gens); + + // No match in the alternate names, so try the common names. We should only + // use the "most specific" common name, which is the last one in the list. + X509_NAME* name = X509_get_subject_name(cert); + int i = -1; + ASN1_STRING* common_name = 0; + while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) + { + X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i); + common_name = X509_NAME_ENTRY_get_data(name_entry); + } + if (common_name && common_name->data && common_name->length) + { + const char* pattern = reinterpret_cast(common_name->data); + std::size_t pattern_length = common_name->length; + if (match_pattern(pattern, pattern_length, host_.c_str())) + return true; + } + + return false; +} + +bool rfc2818_verification::match_pattern(const char* pattern, + std::size_t pattern_length, const char* host) +{ + using namespace std; // For tolower. + + const char* p = pattern; + const char* p_end = p + pattern_length; + const char* h = host; + + while (p != p_end && *h) + { + if (*p == '*') + { + ++p; + while (*h && *h != '.') + if (match_pattern(p, p_end - p, h++)) + return true; + } + else if (tolower(*p) == tolower(*h)) + { + ++p; + ++h; + } + else + { + return false; + } + } + + return p == p_end && !*h; +} + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_IMPL_RFC2818_VERIFICATION_IPP diff --git a/subprojects/asio/include/asio/ssl/impl/src.hpp b/subprojects/asio/include/asio/ssl/impl/src.hpp new file mode 100644 index 000000000..9a1b038a4 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/impl/src.hpp @@ -0,0 +1,28 @@ +// +// impl/ssl/src.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_IMPL_SRC_HPP +#define ASIO_SSL_IMPL_SRC_HPP + +#define ASIO_SOURCE + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HEADER_ONLY) +# error Do not compile Asio library source with ASIO_HEADER_ONLY defined +#endif + +#include "asio/ssl/impl/context.ipp" +#include "asio/ssl/impl/error.ipp" +#include "asio/ssl/detail/impl/engine.ipp" +#include "asio/ssl/detail/impl/openssl_init.ipp" +#include "asio/ssl/impl/rfc2818_verification.ipp" + +#endif // ASIO_SSL_IMPL_SRC_HPP diff --git a/subprojects/asio/include/asio/ssl/rfc2818_verification.hpp b/subprojects/asio/include/asio/ssl/rfc2818_verification.hpp new file mode 100644 index 000000000..3589f5383 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/rfc2818_verification.hpp @@ -0,0 +1,94 @@ +// +// ssl/rfc2818_verification.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_RFC2818_VERIFICATION_HPP +#define ASIO_SSL_RFC2818_VERIFICATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include "asio/ssl/detail/openssl_types.hpp" +#include "asio/ssl/verify_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +/// Verifies a certificate against a hostname according to the rules described +/// in RFC 2818. +/** + * @par Example + * The following example shows how to synchronously open a secure connection to + * a given host name: + * @code + * using asio::ip::tcp; + * namespace ssl = asio::ssl; + * typedef ssl::stream ssl_socket; + * + * // Create a context that uses the default paths for finding CA certificates. + * ssl::context ctx(ssl::context::sslv23); + * ctx.set_default_verify_paths(); + * + * // Open a socket and connect it to the remote host. + * asio::io_context io_context; + * ssl_socket sock(io_context, ctx); + * tcp::resolver resolver(io_context); + * tcp::resolver::query query("host.name", "https"); + * asio::connect(sock.lowest_layer(), resolver.resolve(query)); + * sock.lowest_layer().set_option(tcp::no_delay(true)); + * + * // Perform SSL handshake and verify the remote host's certificate. + * sock.set_verify_mode(ssl::verify_peer); + * sock.set_verify_callback(ssl::rfc2818_verification("host.name")); + * sock.handshake(ssl_socket::client); + * + * // ... read and write as normal ... + * @endcode + */ +class rfc2818_verification +{ +public: + /// The type of the function object's result. + typedef bool result_type; + + /// Constructor. + explicit rfc2818_verification(const std::string& host) + : host_(host) + { + } + + /// Perform certificate verification. + ASIO_DECL bool operator()(bool preverified, verify_context& ctx) const; + +private: + // Helper function to check a host name against a pattern. + ASIO_DECL static bool match_pattern(const char* pattern, + std::size_t pattern_length, const char* host); + + // Helper function to check a host name against an IPv4 address + // The host name to be checked. + std::string host_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/ssl/impl/rfc2818_verification.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_SSL_RFC2818_VERIFICATION_HPP diff --git a/subprojects/asio/include/asio/ssl/stream.hpp b/subprojects/asio/include/asio/ssl/stream.hpp new file mode 100644 index 000000000..a05ee8834 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/stream.hpp @@ -0,0 +1,762 @@ +// +// ssl/stream.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_STREAM_HPP +#define ASIO_SSL_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/async_result.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/ssl/context.hpp" +#include "asio/ssl/detail/buffered_handshake_op.hpp" +#include "asio/ssl/detail/handshake_op.hpp" +#include "asio/ssl/detail/io.hpp" +#include "asio/ssl/detail/read_op.hpp" +#include "asio/ssl/detail/shutdown_op.hpp" +#include "asio/ssl/detail/stream_core.hpp" +#include "asio/ssl/detail/write_op.hpp" +#include "asio/ssl/stream_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +/// Provides stream-oriented functionality using SSL. +/** + * The stream class template provides asynchronous and blocking stream-oriented + * functionality using SSL. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. The application must also ensure that all + * asynchronous operations are performed within the same implicit or explicit + * strand. + * + * @par Example + * To use the SSL stream template with an ip::tcp::socket, you would write: + * @code + * asio::io_context io_context; + * asio::ssl::context ctx(asio::ssl::context::sslv23); + * asio::ssl::stream sock(io_context, ctx); + * @endcode + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class stream : + public stream_base, + private noncopyable +{ +public: + /// The native handle type of the SSL stream. + typedef SSL* native_handle_type; + + /// Structure for use with deprecated impl_type. + struct impl_struct + { + SSL* ssl; + }; + + /// The type of the next layer. + typedef typename remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the executor associated with the object. + typedef typename lowest_layer_type::executor_type executor_type; + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Construct a stream. + /** + * This constructor creates a stream and initialises the underlying stream + * object. + * + * @param arg The argument to be passed to initialise the underlying stream. + * + * @param ctx The SSL context to be used for the stream. + */ + template + stream(Arg&& arg, context& ctx) + : next_layer_(ASIO_MOVE_CAST(Arg)(arg)), + core_(ctx.native_handle(), + next_layer_.lowest_layer().get_executor().context()) + { + } +#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + template + stream(Arg& arg, context& ctx) + : next_layer_(arg), + core_(ctx.native_handle(), + next_layer_.lowest_layer().get_executor().context()) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor. + /** + * @note A @c stream object must not be destroyed while there are pending + * asynchronous operations associated with it. + */ + ~stream() + { + } + + /// Get the executor associated with the object. + /** + * This function may be used to obtain the executor object that the stream + * uses to dispatch handlers for asynchronous operations. + * + * @return A copy of the executor that stream will use to dispatch handlers. + */ + executor_type get_executor() ASIO_NOEXCEPT + { + return next_layer_.lowest_layer().get_executor(); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_context() + { + return next_layer_.lowest_layer().get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + asio::io_context& get_io_service() + { + return next_layer_.lowest_layer().get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to context functionality that is + * not otherwise provided. + * + * @par Example + * The native_handle() function returns a pointer of type @c SSL* that is + * suitable for passing to functions such as @c SSL_get_verify_result and + * @c SSL_get_peer_certificate: + * @code + * asio::ssl::stream sock(io_context, ctx); + * + * // ... establish connection and perform handshake ... + * + * if (X509* cert = SSL_get_peer_certificate(sock.native_handle())) + * { + * if (SSL_get_verify_result(sock.native_handle()) == X509_V_OK) + * { + * // ... + * } + * } + * @endcode + */ + native_handle_type native_handle() + { + return core_.engine_.native_handle(); + } + + /// Get a reference to the next layer. + /** + * This function returns a reference to the next layer in a stack of stream + * layers. + * + * @return A reference to the next layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + const next_layer_type& next_layer() const + { + return next_layer_; + } + + /// Get a reference to the next layer. + /** + * This function returns a reference to the next layer in a stack of stream + * layers. + * + * @return A reference to the next layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * stream layers. + * + * @return A reference to the lowest layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * stream layers. + * + * @return A reference to the lowest layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return next_layer_.lowest_layer(); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the stream. The new mode will override the mode inherited from the context. + * + * @param v A bitmask of peer verification modes. See @ref verify_mode for + * available values. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_set_verify. + */ + void set_verify_mode(verify_mode v) + { + asio::error_code ec; + set_verify_mode(v, ec); + asio::detail::throw_error(ec, "set_verify_mode"); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the stream. The new mode will override the mode inherited from the context. + * + * @param v A bitmask of peer verification modes. See @ref verify_mode for + * available values. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_set_verify. + */ + ASIO_SYNC_OP_VOID set_verify_mode( + verify_mode v, asio::error_code& ec) + { + core_.engine_.set_verify_mode(v, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set the peer verification depth. + /** + * This function may be used to configure the maximum verification depth + * allowed by the stream. + * + * @param depth Maximum depth for the certificate chain verification that + * shall be allowed. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_set_verify_depth. + */ + void set_verify_depth(int depth) + { + asio::error_code ec; + set_verify_depth(depth, ec); + asio::detail::throw_error(ec, "set_verify_depth"); + } + + /// Set the peer verification depth. + /** + * This function may be used to configure the maximum verification depth + * allowed by the stream. + * + * @param depth Maximum depth for the certificate chain verification that + * shall be allowed. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_set_verify_depth. + */ + ASIO_SYNC_OP_VOID set_verify_depth( + int depth, asio::error_code& ec) + { + core_.engine_.set_verify_depth(depth, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set the callback used to verify peer certificates. + /** + * This function is used to specify a callback function that will be called + * by the implementation when it needs to verify a peer certificate. + * + * @param callback The function object to be used for verifying a certificate. + * The function signature of the handler must be: + * @code bool verify_callback( + * bool preverified, // True if the certificate passed pre-verification. + * verify_context& ctx // The peer certificate and other context. + * ); @endcode + * The return value of the callback is true if the certificate has passed + * verification, false otherwise. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls @c SSL_set_verify. + */ + template + void set_verify_callback(VerifyCallback callback) + { + asio::error_code ec; + this->set_verify_callback(callback, ec); + asio::detail::throw_error(ec, "set_verify_callback"); + } + + /// Set the callback used to verify peer certificates. + /** + * This function is used to specify a callback function that will be called + * by the implementation when it needs to verify a peer certificate. + * + * @param callback The function object to be used for verifying a certificate. + * The function signature of the handler must be: + * @code bool verify_callback( + * bool preverified, // True if the certificate passed pre-verification. + * verify_context& ctx // The peer certificate and other context. + * ); @endcode + * The return value of the callback is true if the certificate has passed + * verification, false otherwise. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_set_verify. + */ + template + ASIO_SYNC_OP_VOID set_verify_callback(VerifyCallback callback, + asio::error_code& ec) + { + core_.engine_.set_verify_callback( + new detail::verify_callback(callback), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @throws asio::system_error Thrown on failure. + */ + void handshake(handshake_type type) + { + asio::error_code ec; + handshake(type, ec); + asio::detail::throw_error(ec, "handshake"); + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID handshake(handshake_type type, + asio::error_code& ec) + { + detail::io(next_layer_, core_, detail::handshake_op(type), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param buffers The buffered data to be reused for the handshake. + * + * @throws asio::system_error Thrown on failure. + */ + template + void handshake(handshake_type type, const ConstBufferSequence& buffers) + { + asio::error_code ec; + handshake(type, buffers, ec); + asio::detail::throw_error(ec, "handshake"); + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param buffers The buffered data to be reused for the handshake. + * + * @param ec Set to indicate what error occurred, if any. + */ + template + ASIO_SYNC_OP_VOID handshake(handshake_type type, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + detail::io(next_layer_, core_, + detail::buffered_handshake_op(type, buffers), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous SSL handshake. + /** + * This function is used to asynchronously perform an SSL handshake on the + * stream. This function call always returns immediately. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(HandshakeHandler, + void (asio::error_code)) + async_handshake(handshake_type type, + ASIO_MOVE_ARG(HandshakeHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a HandshakeHandler. + ASIO_HANDSHAKE_HANDLER_CHECK(HandshakeHandler, handler) type_check; + + asio::async_completion init(handler); + + detail::async_io(next_layer_, core_, + detail::handshake_op(type), init.completion_handler); + + return init.result.get(); + } + + /// Start an asynchronous SSL handshake. + /** + * This function is used to asynchronously perform an SSL handshake on the + * stream. This function call always returns immediately. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param buffers The buffered data to be reused for the handshake. Although + * the buffers object may be copied as necessary, ownership of the underlying + * buffers is retained by the caller, which must guarantee that they remain + * valid until the handler is called. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Amount of buffers used in handshake. + * ); @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, + void (asio::error_code, std::size_t)) + async_handshake(handshake_type type, const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a BufferedHandshakeHandler. + ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( + BufferedHandshakeHandler, handler) type_check; + + asio::async_completion init(handler); + + detail::async_io(next_layer_, core_, + detail::buffered_handshake_op(type, buffers), + init.completion_handler); + + return init.result.get(); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @throws asio::system_error Thrown on failure. + */ + void shutdown() + { + asio::error_code ec; + shutdown(ec); + asio::detail::throw_error(ec, "shutdown"); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID shutdown(asio::error_code& ec) + { + detail::io(next_layer_, core_, detail::shutdown_op(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously shut down SSL on the stream. + /** + * This function is used to asynchronously shut down SSL on the stream. This + * function call always returns immediately. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(ShutdownHandler, + void (asio::error_code)) + async_shutdown(ASIO_MOVE_ARG(ShutdownHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ShutdownHandler. + ASIO_SHUTDOWN_HANDLER_CHECK(ShutdownHandler, handler) type_check; + + asio::async_completion init(handler); + + detail::async_io(next_layer_, core_, detail::shutdown_op(), + init.completion_handler); + + return init.result.get(); + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t n = write_some(buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return n; + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written to the stream. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return detail::io(next_layer_, core_, + detail::write_op(buffers), ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write one or more bytes of data to + * the stream. The function call always returns immediately. + * + * @param buffers The data to be written to the stream. Although the buffers + * object may be copied as necessary, ownership of the underlying buffers is + * retained by the caller, which must guarantee that they remain valid until + * the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * + * @note The async_write_some operation may not transmit all of the data to + * the peer. Consider using the @ref async_write function if you need to + * ensure that all data is written before the asynchronous operation + * completes. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + asio::async_completion init(handler); + + detail::async_io(next_layer_, core_, + detail::write_op(buffers), + init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t n = read_some(buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return n; + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return detail::io(next_layer_, core_, + detail::read_op(buffers), ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read one or more bytes of data from + * the stream. The function call always returns immediately. + * + * @param buffers The buffers into which the data will be read. Although the + * buffers object may be copied as necessary, ownership of the underlying + * buffers is retained by the caller, which must guarantee that they remain + * valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * + * @note The async_read_some operation may not read all of the requested + * number of bytes. Consider using the @ref async_read function if you need to + * ensure that the requested amount of data is read before the asynchronous + * operation completes. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + asio::async_completion init(handler); + + detail::async_io(next_layer_, core_, + detail::read_op(buffers), + init.completion_handler); + + return init.result.get(); + } + +private: + Stream next_layer_; + detail::stream_core core_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_HPP diff --git a/subprojects/asio/include/asio/ssl/stream_base.hpp b/subprojects/asio/include/asio/ssl/stream_base.hpp new file mode 100644 index 000000000..56873e4a5 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/stream_base.hpp @@ -0,0 +1,52 @@ +// +// ssl/stream_base.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_STREAM_BASE_HPP +#define ASIO_SSL_STREAM_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +/// The stream_base class is used as a base for the asio::ssl::stream +/// class template so that we have a common place to define various enums. +class stream_base +{ +public: + /// Different handshake types. + enum handshake_type + { + /// Perform handshaking as a client. + client, + + /// Perform handshaking as a server. + server + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~stream_base() + { + } +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_BASE_HPP diff --git a/subprojects/asio/include/asio/ssl/verify_context.hpp b/subprojects/asio/include/asio/ssl/verify_context.hpp new file mode 100644 index 000000000..e17269715 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/verify_context.hpp @@ -0,0 +1,67 @@ +// +// ssl/verify_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_VERIFY_CONTEXT_HPP +#define ASIO_SSL_VERIFY_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +/// A simple wrapper around the X509_STORE_CTX type, used during verification of +/// a peer certificate. +/** + * @note The verify_context does not own the underlying X509_STORE_CTX object. + */ +class verify_context + : private noncopyable +{ +public: + /// The native handle type of the verification context. + typedef X509_STORE_CTX* native_handle_type; + + /// Constructor. + explicit verify_context(native_handle_type handle) + : handle_(handle) + { + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to context functionality that is + * not otherwise provided. + */ + native_handle_type native_handle() + { + return handle_; + } + +private: + // The underlying native implementation. + native_handle_type handle_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_VERIFY_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/ssl/verify_mode.hpp b/subprojects/asio/include/asio/ssl/verify_mode.hpp new file mode 100644 index 000000000..8c4b39426 --- /dev/null +++ b/subprojects/asio/include/asio/ssl/verify_mode.hpp @@ -0,0 +1,63 @@ +// +// ssl/verify_mode.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_VERIFY_MODE_HPP +#define ASIO_SSL_VERIFY_MODE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ssl { + +/// Bitmask type for peer verification. +/** + * Possible values are: + * + * @li @ref verify_none + * @li @ref verify_peer + * @li @ref verify_fail_if_no_peer_cert + * @li @ref verify_client_once + */ +typedef int verify_mode; + +#if defined(GENERATING_DOCUMENTATION) +/// No verification. +const int verify_none = implementation_defined; + +/// Verify the peer. +const int verify_peer = implementation_defined; + +/// Fail verification if the peer has no certificate. Ignored unless +/// @ref verify_peer is set. +const int verify_fail_if_no_peer_cert = implementation_defined; + +/// Do not request client certificate on renegotiation. Ignored unless +/// @ref verify_peer is set. +const int verify_client_once = implementation_defined; +#else +const int verify_none = SSL_VERIFY_NONE; +const int verify_peer = SSL_VERIFY_PEER; +const int verify_fail_if_no_peer_cert = SSL_VERIFY_FAIL_IF_NO_PEER_CERT; +const int verify_client_once = SSL_VERIFY_CLIENT_ONCE; +#endif + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_VERIFY_MODE_HPP diff --git a/subprojects/asio/include/asio/steady_timer.hpp b/subprojects/asio/include/asio/steady_timer.hpp new file mode 100644 index 000000000..3ede20847 --- /dev/null +++ b/subprojects/asio/include/asio/steady_timer.hpp @@ -0,0 +1,42 @@ +// +// steady_timer.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_STEADY_TIMER_HPP +#define ASIO_STEADY_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_waitable_timer.hpp" +#include "asio/detail/chrono.hpp" + +namespace asio { + +/// Typedef for a timer based on the steady clock. +/** + * This typedef uses the C++11 @c <chrono> standard library facility, if + * available. Otherwise, it may use the Boost.Chrono library. To explicitly + * utilise Boost.Chrono, use the basic_waitable_timer template directly: + * @code + * typedef basic_waitable_timer timer; + * @endcode + */ +typedef basic_waitable_timer steady_timer; + +} // namespace asio + +#endif // defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_STEADY_TIMER_HPP diff --git a/subprojects/asio/include/asio/strand.hpp b/subprojects/asio/include/asio/strand.hpp new file mode 100644 index 000000000..ea78ef02f --- /dev/null +++ b/subprojects/asio/include/asio/strand.hpp @@ -0,0 +1,286 @@ +// +// strand.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_STRAND_HPP +#define ASIO_STRAND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/strand_executor_service.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides serialised function invocation for any executor type. +template +class strand +{ +public: + /// The type of the underlying executor. + typedef Executor inner_executor_type; + + /// Default constructor. + /** + * This constructor is only valid if the underlying executor type is default + * constructible. + */ + strand() + : executor_(), + impl_(use_service( + executor_.context()).create_implementation()) + { + } + + /// Construct a strand for the specified executor. + explicit strand(const Executor& e) + : executor_(e), + impl_(use_service( + executor_.context()).create_implementation()) + { + } + + /// Copy constructor. + strand(const strand& other) ASIO_NOEXCEPT + : executor_(other.executor_), + impl_(other.impl_) + { + } + + /// Converting constructor. + /** + * This constructor is only valid if the @c OtherExecutor type is convertible + * to @c Executor. + */ + template + strand( + const strand& other) ASIO_NOEXCEPT + : executor_(other.executor_), + impl_(other.impl_) + { + } + + /// Assignment operator. + strand& operator=(const strand& other) ASIO_NOEXCEPT + { + executor_ = other.executor_; + impl_ = other.impl_; + return *this; + } + + /// Converting assignment operator. + /** + * This assignment operator is only valid if the @c OtherExecutor type is + * convertible to @c Executor. + */ + template + strand& operator=( + const strand& other) ASIO_NOEXCEPT + { + executor_ = other.executor_; + impl_ = other.impl_; + return *this; + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move constructor. + strand(strand&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(Executor)(other.executor_)), + impl_(ASIO_MOVE_CAST(implementation_type)(other.impl_)) + { + } + + /// Converting move constructor. + /** + * This constructor is only valid if the @c OtherExecutor type is convertible + * to @c Executor. + */ + template + strand(strand&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(OtherExecutor)(other)), + impl_(ASIO_MOVE_CAST(implementation_type)(other.impl_)) + { + } + + /// Move assignment operator. + strand& operator=(strand&& other) ASIO_NOEXCEPT + { + executor_ = ASIO_MOVE_CAST(Executor)(other); + impl_ = ASIO_MOVE_CAST(implementation_type)(other.impl_); + return *this; + } + + /// Converting move assignment operator. + /** + * This assignment operator is only valid if the @c OtherExecutor type is + * convertible to @c Executor. + */ + template + strand& operator=( + const strand&& other) ASIO_NOEXCEPT + { + executor_ = ASIO_MOVE_CAST(OtherExecutor)(other); + impl_ = ASIO_MOVE_CAST(implementation_type)(other.impl_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor. + ~strand() + { + } + + /// Obtain the underlying executor. + inner_executor_type get_inner_executor() const ASIO_NOEXCEPT + { + return executor_; + } + + /// Obtain the underlying execution context. + execution_context& context() const ASIO_NOEXCEPT + { + return executor_.context(); + } + + /// Inform the strand that it has some outstanding work to do. + /** + * The strand delegates this call to its underlying executor. + */ + void on_work_started() const ASIO_NOEXCEPT + { + executor_.on_work_started(); + } + + /// Inform the strand that some work is no longer outstanding. + /** + * The strand delegates this call to its underlying executor. + */ + void on_work_finished() const ASIO_NOEXCEPT + { + executor_.on_work_finished(); + } + + /// Request the strand to invoke the given function object. + /** + * This function is used to ask the strand to execute the given function + * object on its underlying executor. The function object will be executed + * inside this function if the strand is not otherwise busy and if the + * underlying executor's @c dispatch() function is also able to execute the + * function before returning. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const + { + detail::strand_executor_service::dispatch(impl_, + executor_, ASIO_MOVE_CAST(Function)(f), a); + } + + /// Request the strand to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object will never be executed inside this function. + * Instead, it will be scheduled by the underlying executor's defer function. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const + { + detail::strand_executor_service::post(impl_, + executor_, ASIO_MOVE_CAST(Function)(f), a); + } + + /// Request the strand to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object will never be executed inside this function. + * Instead, it will be scheduled by the underlying executor's defer function. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const + { + detail::strand_executor_service::defer(impl_, + executor_, ASIO_MOVE_CAST(Function)(f), a); + } + + /// Determine whether the strand is running in the current thread. + /** + * @return @c true if the current thread is executing a function that was + * submitted to the strand using post(), dispatch() or defer(). Otherwise + * returns @c false. + */ + bool running_in_this_thread() const ASIO_NOEXCEPT + { + return detail::strand_executor_service::running_in_this_thread(impl_); + } + + /// Compare two strands for equality. + /** + * Two strands are equal if they refer to the same ordered, non-concurrent + * state. + */ + friend bool operator==(const strand& a, const strand& b) ASIO_NOEXCEPT + { + return a.impl_ == b.impl_; + } + + /// Compare two strands for inequality. + /** + * Two strands are equal if they refer to the same ordered, non-concurrent + * state. + */ + friend bool operator!=(const strand& a, const strand& b) ASIO_NOEXCEPT + { + return a.impl_ != b.impl_; + } + +private: + Executor executor_; + typedef detail::strand_executor_service::implementation_type + implementation_type; + implementation_type impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +// If both io_context.hpp and strand.hpp have been included, automatically +// include the header file needed for the io_context::strand class. +#if !defined(ASIO_NO_EXTENSIONS) +# if defined(ASIO_IO_CONTEXT_HPP) +# include "asio/io_context_strand.hpp" +# endif // defined(ASIO_IO_CONTEXT_HPP) +#endif // !defined(ASIO_NO_EXTENSIONS) + +#endif // ASIO_STRAND_HPP diff --git a/subprojects/asio/include/asio/stream_socket_service.hpp b/subprojects/asio/include/asio/stream_socket_service.hpp new file mode 100644 index 000000000..91b0a711f --- /dev/null +++ b/subprojects/asio/include/asio/stream_socket_service.hpp @@ -0,0 +1,412 @@ +// +// stream_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_STREAM_SOCKET_SERVICE_HPP +#define ASIO_STREAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_ssocket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a stream socket. +template +class stream_socket_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_WINDOWS_RUNTIME) + typedef detail::winrt_ssocket_service service_impl_type; +#elif defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#else + typedef detail::reactive_socket_service service_impl_type; +#endif + +public: + /// The type of a stream socket implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new stream socket service for the specified io_context. + explicit stream_socket_service(asio::io_context& io_context) + : asio::detail::service_base< + stream_socket_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new stream socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new stream socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another stream socket implementation. + void move_assign(implementation_type& impl, + stream_socket_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } + + // All socket services have access to each other's implementations. + template friend class stream_socket_service; + + /// Move-construct a new stream socket implementation from another protocol + /// type. + template + void converting_move_construct(implementation_type& impl, + stream_socket_service& other_service, + typename stream_socket_service< + Protocol1>::implementation_type& other_impl, + typename enable_if::value>::type* = 0) + { + service_impl_.template converting_move_construct( + impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a stream socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a stream socket. + ASIO_SYNC_OP_VOID open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (protocol.type() == ASIO_OS_DEF(SOCK_STREAM)) + service_impl_.open(impl, protocol, ec); + else + ec = asio::error::invalid_argument; + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to a stream socket. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + service_impl_.assign(impl, protocol, native_socket, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a stream socket implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying socket. + native_handle_type release(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.release(impl, ec); + } + + /// Get the native socket implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.at_mark(impl, ec); + } + + /// Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.available(impl, ec); + } + + /// Bind the stream socket to the specified local endpoint. + ASIO_SYNC_OP_VOID bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + service_impl_.bind(impl, endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the stream socket to the specified endpoint. + ASIO_SYNC_OP_VOID connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + service_impl_.connect(impl, peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + template + ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void (asio::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(ConnectHandler) handler) + { + async_completion init(handler); + + service_impl_.async_connect(impl, peer_endpoint, init.completion_handler); + + return init.result.get(); + } + + /// Set a socket option. + template + ASIO_SYNC_OP_VOID set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + service_impl_.set_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get a socket option. + template + ASIO_SYNC_OP_VOID get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + service_impl_.get_option(impl, option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + template + ASIO_SYNC_OP_VOID io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + service_impl_.io_control(impl, command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + bool non_blocking(const implementation_type& impl) const + { + return service_impl_.non_blocking(impl); + } + + /// Sets the non-blocking mode of the socket. + ASIO_SYNC_OP_VOID non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return service_impl_.native_non_blocking(impl); + } + + /// Sets the non-blocking mode of the native socket implementation. + ASIO_SYNC_OP_VOID native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + service_impl_.native_non_blocking(impl, mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.remote_endpoint(impl, ec); + } + + /// Disable sends or receives on the socket. + ASIO_SYNC_OP_VOID shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + service_impl_.shutdown(impl, what, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + ASIO_SYNC_OP_VOID wait(implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + service_impl_.wait(impl, w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, socket_base::wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, w, init.completion_handler); + + return init.result.get(); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send(impl, buffers, flags, ec); + } + + /// Start an asynchronous send. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler) + { + async_completion init(handler); + + service_impl_.async_send(impl, buffers, flags, init.completion_handler); + + return init.result.get(); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive(impl, buffers, flags, ec); + } + + /// Start an asynchronous receive. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler) + { + async_completion init(handler); + + service_impl_.async_receive(impl, buffers, flags, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_STREAM_SOCKET_SERVICE_HPP diff --git a/subprojects/asio/include/asio/streambuf.hpp b/subprojects/asio/include/asio/streambuf.hpp new file mode 100644 index 000000000..cb3f35f7d --- /dev/null +++ b/subprojects/asio/include/asio/streambuf.hpp @@ -0,0 +1,33 @@ +// +// streambuf.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_STREAMBUF_HPP +#define ASIO_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include "asio/basic_streambuf.hpp" + +namespace asio { + +/// Typedef for the typical usage of basic_streambuf. +typedef basic_streambuf<> streambuf; + +} // namespace asio + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_STREAMBUF_HPP diff --git a/subprojects/asio/include/asio/system_context.hpp b/subprojects/asio/include/asio/system_context.hpp new file mode 100644 index 000000000..ccd111343 --- /dev/null +++ b/subprojects/asio/include/asio/system_context.hpp @@ -0,0 +1,78 @@ +// +// system_context.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SYSTEM_CONTEXT_HPP +#define ASIO_SYSTEM_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/scheduler.hpp" +#include "asio/detail/thread_group.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class system_executor; + +/// The executor context for the system executor. +class system_context : public execution_context +{ +public: + /// The executor type associated with the context. + typedef system_executor executor_type; + + /// Destructor shuts down all threads in the system thread pool. + ASIO_DECL ~system_context(); + + /// Obtain an executor for the context. + executor_type get_executor() ASIO_NOEXCEPT; + + /// Signal all threads in the system thread pool to stop. + ASIO_DECL void stop(); + + /// Determine whether the system thread pool has been stopped. + ASIO_DECL bool stopped() const ASIO_NOEXCEPT; + + /// Join all threads in the system thread pool. + ASIO_DECL void join(); + +#if defined(GENERATING_DOCUMENTATION) +private: +#endif // defined(GENERATING_DOCUMENTATION) + // Constructor creates all threads in the system thread pool. + ASIO_DECL system_context(); + +private: + friend class system_executor; + + struct thread_function; + + // The underlying scheduler. + detail::scheduler& scheduler_; + + // The threads in the system thread pool. + detail::thread_group threads_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/system_context.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/system_context.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_SYSTEM_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/system_error.hpp b/subprojects/asio/include/asio/system_error.hpp new file mode 100644 index 000000000..639089416 --- /dev/null +++ b/subprojects/asio/include/asio/system_error.hpp @@ -0,0 +1,131 @@ +// +// system_error.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SYSTEM_ERROR_HPP +#define ASIO_SYSTEM_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) +# include +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) +# include +# include +# include +# include "asio/error_code.hpp" +# include "asio/detail/scoped_ptr.hpp" +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) + +typedef std::system_error system_error; + +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +/// The system_error class is used to represent system conditions that +/// prevent the library from operating correctly. +class system_error + : public std::exception +{ +public: + /// Construct with an error code. + system_error(const error_code& ec) + : code_(ec), + context_() + { + } + + /// Construct with an error code and context. + system_error(const error_code& ec, const std::string& context) + : code_(ec), + context_(context) + { + } + + /// Copy constructor. + system_error(const system_error& other) + : std::exception(other), + code_(other.code_), + context_(other.context_), + what_() + { + } + + /// Destructor. + virtual ~system_error() throw () + { + } + + /// Assignment operator. + system_error& operator=(const system_error& e) + { + context_ = e.context_; + code_ = e.code_; + what_.reset(); + return *this; + } + + /// Get a string representation of the exception. + virtual const char* what() const throw () + { +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + if (!what_.get()) + { + std::string tmp(context_); + if (tmp.length()) + tmp += ": "; + tmp += code_.message(); + what_.reset(new std::string(tmp)); + } + return what_->c_str(); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (std::exception&) + { + return "system_error"; + } +#endif // !defined(ASIO_NO_EXCEPTIONS) + } + + /// Get the error code associated with the exception. + error_code code() const + { + return code_; + } + +private: + // The code associated with the error. + error_code code_; + + // The context associated with the error. + std::string context_; + + // The string representation of the error. + mutable asio::detail::scoped_ptr what_; +}; + +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SYSTEM_ERROR_HPP diff --git a/subprojects/asio/include/asio/system_executor.hpp b/subprojects/asio/include/asio/system_executor.hpp new file mode 100644 index 000000000..b588a21b4 --- /dev/null +++ b/subprojects/asio/include/asio/system_executor.hpp @@ -0,0 +1,129 @@ +// +// system_executor.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SYSTEM_EXECUTOR_HPP +#define ASIO_SYSTEM_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class system_context; + +/// An executor that uses arbitrary threads. +/** + * The system executor represents an execution context where functions are + * permitted to run on arbitrary threads. The post() and defer() functions + * schedule the function to run on an unspecified system thread pool, and + * dispatch() invokes the function immediately. + */ +class system_executor +{ +public: + /// Obtain the underlying execution context. + system_context& context() const ASIO_NOEXCEPT; + + /// Inform the executor that it has some outstanding work to do. + /** + * For the system executor, this is a no-op. + */ + void on_work_started() const ASIO_NOEXCEPT + { + } + + /// Inform the executor that some work is no longer outstanding. + /** + * For the system executor, this is a no-op. + */ + void on_work_finished() const ASIO_NOEXCEPT + { + } + + /// Request the system executor to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object will always be executed inside this function. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the system executor to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object will never be executed inside this function. + * Instead, it will be scheduled to run on an unspecified system thread pool. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the system executor to invoke the given function object. + /** + * This function is used to ask the executor to execute the given function + * object. The function object will never be executed inside this function. + * Instead, it will be scheduled to run on an unspecified system thread pool. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Compare two executors for equality. + /** + * System executors always compare equal. + */ + friend bool operator==(const system_executor&, + const system_executor&) ASIO_NOEXCEPT + { + return true; + } + + /// Compare two executors for inequality. + /** + * System executors always compare equal. + */ + friend bool operator!=(const system_executor&, + const system_executor&) ASIO_NOEXCEPT + { + return false; + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/system_executor.hpp" + +#endif // ASIO_SYSTEM_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/system_timer.hpp b/subprojects/asio/include/asio/system_timer.hpp new file mode 100644 index 000000000..e75e7d4c1 --- /dev/null +++ b/subprojects/asio/include/asio/system_timer.hpp @@ -0,0 +1,42 @@ +// +// system_timer.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SYSTEM_TIMER_HPP +#define ASIO_SYSTEM_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_waitable_timer.hpp" +#include "asio/detail/chrono.hpp" + +namespace asio { + +/// Typedef for a timer based on the system clock. +/** + * This typedef uses the C++11 @c <chrono> standard library facility, if + * available. Otherwise, it may use the Boost.Chrono library. To explicitly + * utilise Boost.Chrono, use the basic_waitable_timer template directly: + * @code + * typedef basic_waitable_timer timer; + * @endcode + */ +typedef basic_waitable_timer system_timer; + +} // namespace asio + +#endif // defined(ASIO_HAS_CHRONO) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_SYSTEM_TIMER_HPP diff --git a/subprojects/asio/include/asio/thread.hpp b/subprojects/asio/include/asio/thread.hpp new file mode 100644 index 000000000..eeeef7bb5 --- /dev/null +++ b/subprojects/asio/include/asio/thread.hpp @@ -0,0 +1,92 @@ +// +// thread.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_THREAD_HPP +#define ASIO_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/thread.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// A simple abstraction for starting threads. +/** + * The asio::thread class implements the smallest possible subset of the + * functionality of boost::thread. It is intended to be used only for starting + * a thread and waiting for it to exit. If more extensive threading + * capabilities are required, you are strongly advised to use something else. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * A typical use of asio::thread would be to launch a thread to run an + * io_context's event processing loop: + * + * @par + * @code asio::io_context io_context; + * // ... + * asio::thread t(boost::bind(&asio::io_context::run, &io_context)); + * // ... + * t.join(); @endcode + */ +class thread + : private noncopyable +{ +public: + /// Start a new thread that executes the supplied function. + /** + * This constructor creates a new thread that will execute the given function + * or function object. + * + * @param f The function or function object to be run in the thread. The + * function signature must be: @code void f(); @endcode + */ + template + explicit thread(Function f) + : impl_(f) + { + } + + /// Destructor. + ~thread() + { + } + + /// Wait for the thread to exit. + /** + * This function will block until the thread has exited. + * + * If this function is not called before the thread object is destroyed, the + * thread itself will continue to run until completion. You will, however, + * no longer have the ability to wait for it to exit. + */ + void join() + { + impl_.join(); + } + +private: + detail::thread impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_THREAD_HPP diff --git a/subprojects/asio/include/asio/thread_pool.hpp b/subprojects/asio/include/asio/thread_pool.hpp new file mode 100644 index 000000000..1a86bcd5b --- /dev/null +++ b/subprojects/asio/include/asio/thread_pool.hpp @@ -0,0 +1,232 @@ +// +// thread_pool.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_THREAD_POOL_HPP +#define ASIO_THREAD_POOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scheduler.hpp" +#include "asio/detail/thread_group.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// A simple fixed-size thread pool. +/** + * The thread pool class is an execution context where functions are permitted + * to run on one of a fixed number of threads. + * + * @par Submitting tasks to the pool + * + * To submit functions to the thread_pool, use the @ref asio::dispatch, + * @ref asio::post or @ref asio::defer free functions. + * + * For example: + * + * @code void my_task() + * { + * ... + * } + * + * ... + * + * // Launch the pool with four threads. + * asio::thread_pool pool(4); + * + * // Submit a function to the pool. + * asio::post(pool, my_task); + * + * // Submit a lambda object to the pool. + * asio::post(pool, + * []() + * { + * ... + * }); + * + * // Wait for all tasks in the pool to complete. + * pool.join(); @endcode + */ +class thread_pool + : public execution_context +{ +public: + class executor_type; + + /// Constructs a pool with an automatically determined number of threads. + ASIO_DECL thread_pool(); + + /// Constructs a pool with a specified number of threads. + ASIO_DECL thread_pool(std::size_t num_threads); + + /// Destructor. + /** + * Automatically stops and joins the pool, if not explicitly done beforehand. + */ + ASIO_DECL ~thread_pool(); + + /// Obtains the executor associated with the pool. + executor_type get_executor() ASIO_NOEXCEPT; + + /// Stops the threads. + /** + * This function stops the threads as soon as possible. As a result of calling + * @c stop(), pending function objects may be never be invoked. + */ + ASIO_DECL void stop(); + + /// Joins the threads. + /** + * This function blocks until the threads in the pool have completed. If @c + * stop() is not called prior to @c join(), the @c join() call will wait + * until the pool has no more outstanding work. + */ + ASIO_DECL void join(); + +private: + friend class executor_type; + struct thread_function; + + // The underlying scheduler. + detail::scheduler& scheduler_; + + // The threads in the pool. + detail::thread_group threads_; +}; + +/// Executor used to submit functions to a thread pool. +class thread_pool::executor_type +{ +public: + /// Obtain the underlying execution context. + thread_pool& context() const ASIO_NOEXCEPT; + + /// Inform the thread pool that it has some outstanding work to do. + /** + * This function is used to inform the thread pool that some work has begun. + * This ensures that the thread pool's join() function will not return while + * the work is underway. + */ + void on_work_started() const ASIO_NOEXCEPT; + + /// Inform the thread pool that some work is no longer outstanding. + /** + * This function is used to inform the thread pool that some work has + * finished. Once the count of unfinished work reaches zero, the thread + * pool's join() function is permitted to exit. + */ + void on_work_finished() const ASIO_NOEXCEPT; + + /// Request the thread pool to invoke the given function object. + /** + * This function is used to ask the thread pool to execute the given function + * object. If the current thread belongs to the pool, @c dispatch() executes + * the function before returning. Otherwise, the function will be scheduled + * to run on the thread pool. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the thread pool to invoke the given function object. + /** + * This function is used to ask the thread pool to execute the given function + * object. The function object will never be executed inside @c post(). + * Instead, it will be scheduled to run on the thread pool. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Request the thread pool to invoke the given function object. + /** + * This function is used to ask the thread pool to execute the given function + * object. The function object will never be executed inside @c defer(). + * Instead, it will be scheduled to run on the thread pool. + * + * If the current thread belongs to the thread pool, @c defer() will delay + * scheduling the function object until the current thread returns control to + * the pool. + * + * @param f The function object to be called. The executor will make + * a copy of the handler object as required. The function signature of the + * function object must be: @code void function(); @endcode + * + * @param a An allocator that may be used by the executor to allocate the + * internal storage needed for function invocation. + */ + template + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; + + /// Determine whether the thread pool is running in the current thread. + /** + * @return @c true if the current thread belongs to the pool. Otherwise + * returns @c false. + */ + bool running_in_this_thread() const ASIO_NOEXCEPT; + + /// Compare two executors for equality. + /** + * Two executors are equal if they refer to the same underlying thread pool. + */ + friend bool operator==(const executor_type& a, + const executor_type& b) ASIO_NOEXCEPT + { + return &a.pool_ == &b.pool_; + } + + /// Compare two executors for inequality. + /** + * Two executors are equal if they refer to the same underlying thread pool. + */ + friend bool operator!=(const executor_type& a, + const executor_type& b) ASIO_NOEXCEPT + { + return &a.pool_ != &b.pool_; + } + +private: + friend class thread_pool; + + // Constructor. + explicit executor_type(thread_pool& p) : pool_(p) {} + + // The underlying thread pool. + thread_pool& pool_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/thread_pool.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/thread_pool.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_THREAD_POOL_HPP diff --git a/subprojects/asio/include/asio/time_traits.hpp b/subprojects/asio/include/asio/time_traits.hpp new file mode 100644 index 000000000..72f4aabe7 --- /dev/null +++ b/subprojects/asio/include/asio/time_traits.hpp @@ -0,0 +1,86 @@ +// +// time_traits.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TIME_TRAITS_HPP +#define ASIO_TIME_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Time traits suitable for use with the deadline timer. +template +struct time_traits; + +/// Time traits specialised for posix_time. +template <> +struct time_traits +{ + /// The time type. + typedef boost::posix_time::ptime time_type; + + /// The duration type. + typedef boost::posix_time::time_duration duration_type; + + /// Get the current time. + static time_type now() + { +#if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) + return boost::posix_time::microsec_clock::universal_time(); +#else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) + return boost::posix_time::second_clock::universal_time(); +#endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) + } + + /// Add a duration to a time. + static time_type add(const time_type& t, const duration_type& d) + { + return t + d; + } + + /// Subtract one time from another. + static duration_type subtract(const time_type& t1, const time_type& t2) + { + return t1 - t2; + } + + /// Test whether one time is less than another. + static bool less_than(const time_type& t1, const time_type& t2) + { + return t1 < t2; + } + + /// Convert to POSIX duration type. + static boost::posix_time::time_duration to_posix_duration( + const duration_type& d) + { + return d; + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_TIME_TRAITS_HPP diff --git a/subprojects/asio/include/asio/ts/buffer.hpp b/subprojects/asio/include/asio/ts/buffer.hpp new file mode 100644 index 000000000..faf08a474 --- /dev/null +++ b/subprojects/asio/include/asio/ts/buffer.hpp @@ -0,0 +1,24 @@ +// +// ts/buffer.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_BUFFER_HPP +#define ASIO_TS_BUFFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/read.hpp" +#include "asio/write.hpp" +#include "asio/read_until.hpp" + +#endif // ASIO_TS_BUFFER_HPP diff --git a/subprojects/asio/include/asio/ts/executor.hpp b/subprojects/asio/include/asio/ts/executor.hpp new file mode 100644 index 000000000..8669cb6c5 --- /dev/null +++ b/subprojects/asio/include/asio/ts/executor.hpp @@ -0,0 +1,35 @@ +// +// ts/executor.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_EXECUTOR_HPP +#define ASIO_TS_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/handler_type.hpp" +#include "asio/async_result.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/execution_context.hpp" +#include "asio/is_executor.hpp" +#include "asio/associated_executor.hpp" +#include "asio/bind_executor.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/system_executor.hpp" +#include "asio/executor.hpp" +#include "asio/dispatch.hpp" +#include "asio/post.hpp" +#include "asio/defer.hpp" +#include "asio/strand.hpp" +#include "asio/packaged_task.hpp" +#include "asio/use_future.hpp" + +#endif // ASIO_TS_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/ts/internet.hpp b/subprojects/asio/include/asio/ts/internet.hpp new file mode 100644 index 000000000..6282e905a --- /dev/null +++ b/subprojects/asio/include/asio/ts/internet.hpp @@ -0,0 +1,40 @@ +// +// ts/internet.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_INTERNET_HPP +#define ASIO_TS_INTERNET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/ip/address.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v4_iterator.hpp" +#include "asio/ip/address_v4_range.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/ip/address_v6_iterator.hpp" +#include "asio/ip/address_v6_range.hpp" +#include "asio/ip/bad_address_cast.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_entry.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/host_name.hpp" +#include "asio/ip/network_v4.hpp" +#include "asio/ip/network_v6.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/ip/udp.hpp" +#include "asio/ip/v6_only.hpp" +#include "asio/ip/unicast.hpp" +#include "asio/ip/multicast.hpp" + +#endif // ASIO_TS_INTERNET_HPP diff --git a/subprojects/asio/include/asio/ts/io_context.hpp b/subprojects/asio/include/asio/ts/io_context.hpp new file mode 100644 index 000000000..b967cd63e --- /dev/null +++ b/subprojects/asio/include/asio/ts/io_context.hpp @@ -0,0 +1,20 @@ +// +// ts/io_context.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_IO_CONTEXT_HPP +#define ASIO_TS_IO_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/io_context.hpp" + +#endif // ASIO_TS_IO_CONTEXT_HPP diff --git a/subprojects/asio/include/asio/ts/net.hpp b/subprojects/asio/include/asio/ts/net.hpp new file mode 100644 index 000000000..f96075d9f --- /dev/null +++ b/subprojects/asio/include/asio/ts/net.hpp @@ -0,0 +1,26 @@ +// +// ts/net.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_NET_HPP +#define ASIO_TS_NET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/ts/netfwd.hpp" +#include "asio/ts/executor.hpp" +#include "asio/ts/io_context.hpp" +#include "asio/ts/timer.hpp" +#include "asio/ts/buffer.hpp" +#include "asio/ts/socket.hpp" +#include "asio/ts/internet.hpp" + +#endif // ASIO_TS_NET_HPP diff --git a/subprojects/asio/include/asio/ts/netfwd.hpp b/subprojects/asio/include/asio/ts/netfwd.hpp new file mode 100644 index 000000000..657a21d37 --- /dev/null +++ b/subprojects/asio/include/asio/ts/netfwd.hpp @@ -0,0 +1,197 @@ +// +// ts/netfwd.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_NETFWD_HPP +#define ASIO_TS_NETFWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CHRONO) +# include "asio/detail/chrono.hpp" +#endif // defined(ASIO_HAS_CHRONO) + +#if defined(ASIO_HAS_BOOST_DATE_TIME) +# include "asio/detail/date_time_fwd.hpp" +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#if !defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class execution_context; + +template +class executor_binder; + +template +class executor_work_guard; + +class system_executor; + +class executor; + +template +class strand; + +class io_context; + +template +struct wait_traits; + +#if defined(ASIO_HAS_BOOST_DATE_TIME) + +template +struct time_traits; + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +template +class waitable_timer_service; + +#if defined(ASIO_HAS_BOOST_DATE_TIME) + +template +class deadline_timer_service; + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#if !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) +#define ASIO_BASIC_WAITABLE_TIMER_FWD_DECL + +template + ASIO_SVC_TPARAM_DEF2(= waitable_timer_service)> +class basic_waitable_timer; + +#endif // !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) + +#if defined(ASIO_HAS_CHRONO) + +typedef basic_waitable_timer system_timer; + +typedef basic_waitable_timer steady_timer; + +typedef basic_waitable_timer + high_resolution_timer; + +#endif // defined(ASIO_HAS_CHRONO) + +template +class basic_socket; + +template +class basic_datagram_socket; + +template +class basic_stream_socket; + +template +class basic_socket_acceptor; + +#if !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) +#define ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL + +// Forward declaration with defaulted arguments. +template ), +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + typename Clock = boost::posix_time::ptime, + typename WaitTraits = time_traits + ASIO_SVC_TPARAM1_DEF2(= deadline_timer_service)> +#else + typename Clock = chrono::steady_clock, + typename WaitTraits = wait_traits + ASIO_SVC_TPARAM1_DEF1(= steady_timer::service_type)> +#endif +class basic_socket_streambuf; + +#endif // !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) + +#if !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) +#define ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL + +// Forward declaration with defaulted arguments. +template ), +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + typename Clock = boost::posix_time::ptime, + typename WaitTraits = time_traits + ASIO_SVC_TPARAM1_DEF2(= deadline_timer_service)> +#else + typename Clock = chrono::steady_clock, + typename WaitTraits = wait_traits + ASIO_SVC_TPARAM1_DEF1(= steady_timer::service_type)> +#endif +class basic_socket_iostream; + +#endif // !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) + +namespace ip { + +class address; + +class address_v4; + +class address_v6; + +template +class basic_address_iterator; + +typedef basic_address_iterator address_v4_iterator; + +typedef basic_address_iterator address_v6_iterator; + +template +class basic_address_range; + +typedef basic_address_range address_v4_range; + +typedef basic_address_range address_v6_range; + +class network_v4; + +class network_v6; + +template +class basic_endpoint; + +template +class basic_resolver_entry; + +template +class basic_resolver_results; + +template +class basic_resolver; + +class tcp; + +class udp; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_TS_NETFWD_HPP diff --git a/subprojects/asio/include/asio/ts/socket.hpp b/subprojects/asio/include/asio/ts/socket.hpp new file mode 100644 index 000000000..a54273485 --- /dev/null +++ b/subprojects/asio/include/asio/ts/socket.hpp @@ -0,0 +1,27 @@ +// +// ts/socket.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_SOCKET_HPP +#define ASIO_TS_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/socket_base.hpp" +#include "asio/basic_socket.hpp" +#include "asio/basic_datagram_socket.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_streambuf.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/connect.hpp" + +#endif // ASIO_TS_SOCKET_HPP diff --git a/subprojects/asio/include/asio/ts/timer.hpp b/subprojects/asio/include/asio/ts/timer.hpp new file mode 100644 index 000000000..872be8bfd --- /dev/null +++ b/subprojects/asio/include/asio/ts/timer.hpp @@ -0,0 +1,26 @@ +// +// ts/timer.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TS_TIMER_HPP +#define ASIO_TS_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/chrono.hpp" + +#include "asio/wait_traits.hpp" +#include "asio/basic_waitable_timer.hpp" +#include "asio/system_timer.hpp" +#include "asio/steady_timer.hpp" +#include "asio/high_resolution_timer.hpp" + +#endif // ASIO_TS_TIMER_HPP diff --git a/subprojects/asio/include/asio/unyield.hpp b/subprojects/asio/include/asio/unyield.hpp new file mode 100644 index 000000000..de3ed0275 --- /dev/null +++ b/subprojects/asio/include/asio/unyield.hpp @@ -0,0 +1,21 @@ +// +// unyield.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifdef reenter +# undef reenter +#endif + +#ifdef yield +# undef yield +#endif + +#ifdef fork +# undef fork +#endif diff --git a/subprojects/asio/include/asio/use_future.hpp b/subprojects/asio/include/asio/use_future.hpp new file mode 100644 index 000000000..3ac95a562 --- /dev/null +++ b/subprojects/asio/include/asio/use_future.hpp @@ -0,0 +1,160 @@ +// +// use_future.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_USE_FUTURE_HPP +#define ASIO_USE_FUTURE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/future.hpp" + +#if defined(ASIO_HAS_STD_FUTURE_CLASS) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class packaged_token; + +template +class packaged_handler; + +} // namespace detail + +/// Class used to specify that an asynchronous operation should return a future. +/** + * The use_future_t class is used to indicate that an asynchronous operation + * should return a std::future object. A use_future_t object may be passed as a + * handler to an asynchronous operation, typically using the special value @c + * asio::use_future. For example: + * + * @code std::future my_future + * = my_socket.async_read_some(my_buffer, asio::use_future); @endcode + * + * The initiating function (async_read_some in the above example) returns a + * future that will receive the result of the operation. If the operation + * completes with an error_code indicating failure, it is converted into a + * system_error and passed back to the caller via the future. + */ +template > +class use_future_t +{ +public: + /// The allocator type. The allocator is used when constructing the + /// @c std::promise object for a given asynchronous operation. + typedef Allocator allocator_type; + + /// Construct using default-constructed allocator. + ASIO_CONSTEXPR use_future_t() + { + } + + /// Construct using specified allocator. + explicit use_future_t(const Allocator& allocator) + : allocator_(allocator) + { + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use rebind().) Specify an alternate allocator. + template + use_future_t operator[](const OtherAllocator& allocator) const + { + return use_future_t(allocator); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Specify an alternate allocator. + template + use_future_t rebind(const OtherAllocator& allocator) const + { + return use_future_t(allocator); + } + + /// Obtain allocator. + allocator_type get_allocator() const + { + return allocator_; + } + + /// Wrap a function object in a packaged task. + /** + * The @c package function is used to adapt a function object as a packaged + * task. When this adapter is passed as a completion token to an asynchronous + * operation, the result of the function object is retuned via a std::future. + * + * @par Example + * + * @code std::future fut = + * my_socket.async_read_some(buffer, + * use_future([](asio::error_code ec, std::size_t n) + * { + * return ec ? 0 : n; + * })); + * ... + * std::size_t n = fut.get(); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else // defined(GENERATING_DOCUMENTATION) + detail::packaged_token::type, Allocator> +#endif // defined(GENERATING_DOCUMENTATION) + operator()(ASIO_MOVE_ARG(Function) f) const; + +private: + // Helper type to ensure that use_future can be constexpr default-constructed + // even when std::allocator can't be. + struct std_allocator_void + { + ASIO_CONSTEXPR std_allocator_void() + { + } + + operator std::allocator() const + { + return std::allocator(); + } + }; + + typename conditional< + is_same, Allocator>::value, + std_allocator_void, Allocator>::type allocator_; +}; + +/// A special value, similar to std::nothrow. +/** + * See the documentation for asio::use_future_t for a usage example. + */ +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr use_future_t<> use_future; +#elif defined(ASIO_MSVC) +__declspec(selectany) use_future_t<> use_future; +#endif + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/use_future.hpp" + +#endif // defined(ASIO_HAS_STD_FUTURE_CLASS) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_USE_FUTURE_HPP diff --git a/subprojects/asio/include/asio/uses_executor.hpp b/subprojects/asio/include/asio/uses_executor.hpp new file mode 100644 index 000000000..e985c2824 --- /dev/null +++ b/subprojects/asio/include/asio/uses_executor.hpp @@ -0,0 +1,71 @@ +// +// uses_executor.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_USES_EXECUTOR_HPP +#define ASIO_USES_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// A special type, similar to std::nothrow_t, used to disambiguate +/// constructors that accept executor arguments. +/** + * The executor_arg_t struct is an empty structure type used as a unique type + * to disambiguate constructor and function overloading. Specifically, some + * types have constructors with executor_arg_t as the first argument, + * immediately followed by an argument of a type that satisfies the Executor + * type requirements. + */ +struct executor_arg_t +{ + /// Constructor. + ASIO_CONSTEXPR executor_arg_t() ASIO_NOEXCEPT + { + } +}; + +/// A special value, similar to std::nothrow, used to disambiguate constructors +/// that accept executor arguments. +/** + * See asio::executor_arg_t and asio::uses_executor + * for more information. + */ +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr executor_arg_t executor_arg; +#elif defined(ASIO_MSVC) +__declspec(selectany) executor_arg_t executor_arg; +#endif + +/// The uses_executor trait detects whether a type T has an associated executor +/// that is convertible from type Executor. +/** + * Meets the BinaryTypeTrait requirements. The Asio library provides a + * definition that is derived from false_type. A program may specialize this + * template to derive from true_type for a user-defined type T that can be + * constructed with an executor, where the first argument of a constructor has + * type executor_arg_t and the second argument is convertible from type + * Executor. + */ +template +struct uses_executor : false_type {}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_USES_EXECUTOR_HPP diff --git a/subprojects/asio/include/asio/version.hpp b/subprojects/asio/include/asio/version.hpp new file mode 100644 index 000000000..82ceb1682 --- /dev/null +++ b/subprojects/asio/include/asio/version.hpp @@ -0,0 +1,23 @@ +// +// version.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_VERSION_HPP +#define ASIO_VERSION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +// ASIO_VERSION % 100 is the sub-minor version +// ASIO_VERSION / 100 % 1000 is the minor version +// ASIO_VERSION / 100000 is the major version +#define ASIO_VERSION 101202 // 1.12.2 + +#endif // ASIO_VERSION_HPP diff --git a/subprojects/asio/include/asio/wait_traits.hpp b/subprojects/asio/include/asio/wait_traits.hpp new file mode 100644 index 000000000..a6016f7c0 --- /dev/null +++ b/subprojects/asio/include/asio/wait_traits.hpp @@ -0,0 +1,56 @@ +// +// wait_traits.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WAIT_TRAITS_HPP +#define ASIO_WAIT_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Wait traits suitable for use with the basic_waitable_timer class template. +template +struct wait_traits +{ + /// Convert a clock duration into a duration used for waiting. + /** + * @returns @c d. + */ + static typename Clock::duration to_wait_duration( + const typename Clock::duration& d) + { + return d; + } + + /// Convert a clock duration into a duration used for waiting. + /** + * @returns @c d. + */ + static typename Clock::duration to_wait_duration( + const typename Clock::time_point& t) + { + typename Clock::time_point now = Clock::now(); + if (now + (Clock::duration::max)() < t) + return (Clock::duration::max)(); + if (now + (Clock::duration::min)() > t) + return (Clock::duration::min)(); + return t - now; + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WAIT_TRAITS_HPP diff --git a/subprojects/asio/include/asio/waitable_timer_service.hpp b/subprojects/asio/include/asio/waitable_timer_service.hpp new file mode 100644 index 000000000..75e496f8d --- /dev/null +++ b/subprojects/asio/include/asio/waitable_timer_service.hpp @@ -0,0 +1,210 @@ +// +// waitable_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WAITABLE_TIMER_SERVICE_HPP +#define ASIO_WAITABLE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/chrono_time_traits.hpp" +#include "asio/detail/deadline_timer_service.hpp" +#include "asio/io_context.hpp" +#include "asio/wait_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default service implementation for a timer. +template > +class waitable_timer_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base< + waitable_timer_service > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + + /// The clock type. + typedef Clock clock_type; + + /// The duration type of the clock. + typedef typename clock_type::duration duration; + + /// The time point type of the clock. + typedef typename clock_type::time_point time_point; + + /// The wait traits type. + typedef WaitTraits traits_type; + +private: + // The type of the platform-specific implementation. + typedef detail::deadline_timer_service< + detail::chrono_time_traits > service_impl_type; + +public: + /// The implementation type of the waitable timer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new timer service for the specified io_context. + explicit waitable_timer_service(asio::io_context& io_context) + : asio::detail::service_base< + waitable_timer_service >(io_context), + service_impl_(io_context) + { + } + + /// Construct a new timer implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a timer implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new timer implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another timer implementation. + void move_assign(implementation_type& impl, + waitable_timer_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl, asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Cancels one asynchronous wait operation associated with the timer. + std::size_t cancel_one(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel_one(impl, ec); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the expiry time for the timer as an + /// absolute time. + time_point expires_at(const implementation_type& impl) const + { + return service_impl_.expiry(impl); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the expiry time for the timer as an absolute time. + time_point expiry(const implementation_type& impl) const + { + return service_impl_.expiry(impl); + } + + /// Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_point& expiry_time, asio::error_code& ec) + { + return service_impl_.expires_at(impl, expiry_time, ec); + } + + /// Set the expiry time for the timer relative to now. + std::size_t expires_after(implementation_type& impl, + const duration& expiry_time, asio::error_code& ec) + { + return service_impl_.expires_after(impl, expiry_time, ec); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the expiry time for the timer relative to + /// now. + duration expires_from_now(const implementation_type& impl) const + { + typedef detail::chrono_time_traits traits; + return traits::subtract(service_impl_.expiry(impl), traits::now()); + } + + /// (Deprecated: Use expires_after().) Set the expiry time for the timer + /// relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration& expiry_time, asio::error_code& ec) + { + return service_impl_.expires_after(impl, expiry_time, ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl, asio::error_code& ec) + { + service_impl_.wait(impl, ec); + } + + // Start an asynchronous wait on the timer. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, + ASIO_MOVE_ARG(WaitHandler) handler) + { + async_completion init(handler); + + service_impl_.async_wait(impl, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WAITABLE_TIMER_SERVICE_HPP diff --git a/subprojects/asio/include/asio/windows/basic_handle.hpp b/subprojects/asio/include/asio/windows/basic_handle.hpp new file mode 100644 index 000000000..f7c9d0d03 --- /dev/null +++ b/subprojects/asio/include/asio/windows/basic_handle.hpp @@ -0,0 +1,273 @@ +// +// windows/basic_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_BASIC_HANDLE_HPP +#define ASIO_WINDOWS_BASIC_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Provides Windows handle functionality. +/** + * The windows::basic_handle class template provides the ability to wrap a + * Windows handle. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_handle + : public basic_io_object +{ +public: + /// The native representation of a handle. + typedef typename HandleService::native_handle_type native_handle_type; + + /// A basic_handle is always the lowest layer. + typedef basic_handle lowest_layer_type; + + /// Construct a basic_handle without opening it. + /** + * This constructor creates a handle without opening it. + * + * @param io_context The io_context object that the handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit basic_handle(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct a basic_handle on an existing native handle. + /** + * This constructor creates a handle object to hold an existing native handle. + * + * @param io_context The io_context object that the handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param handle A native handle. + * + * @throws asio::system_error Thrown on failure. + */ + basic_handle(asio::io_context& io_context, + const native_handle_type& handle) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), handle, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_handle from another. + /** + * This constructor moves a handle from one object to another. + * + * @param other The other basic_handle object from which the move will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_handle(io_context&) constructor. + */ + basic_handle(basic_handle&& other) + : basic_io_object( + ASIO_MOVE_CAST(basic_handle)(other)) + { + } + + /// Move-assign a basic_handle from another. + /** + * This assignment operator moves a handle from one object to another. + * + * @param other The other basic_handle object from which the move will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_handle(io_context&) constructor. + */ + basic_handle& operator=(basic_handle&& other) + { + basic_io_object::operator=( + ASIO_MOVE_CAST(basic_handle)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_handle cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_handle cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param handle A native handle. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& handle) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), handle, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param handle A native handle. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& handle, + asio::error_code& ec) + { + this->get_service().assign(this->get_implementation(), handle, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the handle is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native handle representation. + /** + * This function may be used to obtain the underlying representation of the + * handle. This is intended to allow access to native handle functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + +protected: + /// Protected destructor to prevent deletion through this type. + ~basic_handle() + { + } +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_BASIC_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/basic_object_handle.hpp b/subprojects/asio/include/asio/windows/basic_object_handle.hpp new file mode 100644 index 000000000..4b2968460 --- /dev/null +++ b/subprojects/asio/include/asio/windows/basic_object_handle.hpp @@ -0,0 +1,182 @@ +// +// windows/basic_object_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2011 Boris Schaeling (boris@highscore.de) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_BASIC_OBJECT_HANDLE_HPP +#define ASIO_WINDOWS_BASIC_OBJECT_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/windows/basic_handle.hpp" +#include "asio/windows/object_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Provides object-oriented handle functionality. +/** + * The windows::basic_object_handle class template provides asynchronous and + * blocking object-oriented handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_object_handle + : public basic_handle +{ +public: + /// The native representation of a handle. + typedef typename ObjectHandleService::native_handle_type native_handle_type; + + /// Construct a basic_object_handle without opening it. + /** + * This constructor creates an object handle without opening it. + * + * @param io_context The io_context object that the object handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit basic_object_handle(asio::io_context& io_context) + : basic_handle(io_context) + { + } + + /// Construct a basic_object_handle on an existing native handle. + /** + * This constructor creates an object handle object to hold an existing native + * handle. + * + * @param io_context The io_context object that the object handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param native_handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_object_handle(asio::io_context& io_context, + const native_handle_type& native_handle) + : basic_handle(io_context, native_handle) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_object_handle from another. + /** + * This constructor moves an object handle from one object to another. + * + * @param other The other basic_object_handle object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_object_handle(io_context&) constructor. + */ + basic_object_handle(basic_object_handle&& other) + : basic_handle( + ASIO_MOVE_CAST(basic_object_handle)(other)) + { + } + + /// Move-assign a basic_object_handle from another. + /** + * This assignment operator moves an object handle from one object to another. + * + * @param other The other basic_object_handle object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_object_handle(io_context&) constructor. + */ + basic_object_handle& operator=(basic_object_handle&& other) + { + basic_handle::operator=( + ASIO_MOVE_CAST(basic_object_handle)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Perform a blocking wait on the object handle. + /** + * This function is used to wait for the object handle to be set to the + * signalled state. This function blocks and does not return until the object + * handle has been set to the signalled state. + * + * @throws asio::system_error Thrown on failure. + */ + void wait() + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Perform a blocking wait on the object handle. + /** + * This function is used to wait for the object handle to be set to the + * signalled state. This function blocks and does not return until the object + * handle has been set to the signalled state. + * + * @param ec Set to indicate what error occurred, if any. + */ + void wait(asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), ec); + } + + /// Start an asynchronous wait on the object handle. + /** + * This function is be used to initiate an asynchronous wait against the + * object handle. It always returns immediately. + * + * @param handler The handler to be called when the object handle is set to + * the signalled state. Copies will be made of the handler as required. The + * function signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(ASIO_MOVE_ARG(WaitHandler) handler) + { + return this->get_service().async_wait(this->get_implementation(), + ASIO_MOVE_CAST(WaitHandler)(handler)); + } +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_BASIC_OBJECT_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/basic_random_access_handle.hpp b/subprojects/asio/include/asio/windows/basic_random_access_handle.hpp new file mode 100644 index 000000000..422697731 --- /dev/null +++ b/subprojects/asio/include/asio/windows/basic_random_access_handle.hpp @@ -0,0 +1,376 @@ +// +// windows/basic_random_access_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP +#define ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/windows/basic_handle.hpp" +#include "asio/windows/random_access_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Provides random-access handle functionality. +/** + * The windows::basic_random_access_handle class template provides asynchronous + * and blocking random-access handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_random_access_handle + : public basic_handle +{ +public: + /// The native representation of a handle. + typedef typename RandomAccessHandleService::native_handle_type + native_handle_type; + + /// Construct a basic_random_access_handle without opening it. + /** + * This constructor creates a random-access handle without opening it. The + * handle needs to be opened before data can be written to or read from it. + * + * @param io_context The io_context object that the random-access handle will + * use to dispatch handlers for any asynchronous operations performed on the + * handle. + */ + explicit basic_random_access_handle(asio::io_context& io_context) + : basic_handle(io_context) + { + } + + /// Construct a basic_random_access_handle on an existing native handle. + /** + * This constructor creates a random-access handle object to hold an existing + * native handle. + * + * @param io_context The io_context object that the random-access handle will + * use to dispatch handlers for any asynchronous operations performed on the + * handle. + * + * @param handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_random_access_handle(asio::io_context& io_context, + const native_handle_type& handle) + : basic_handle(io_context, handle) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_random_access_handle from another. + /** + * This constructor moves a random-access handle from one object to another. + * + * @param other The other basic_random_access_handle object from which the + * move will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_random_access_handle(io_context&) + * constructor. + */ + basic_random_access_handle(basic_random_access_handle&& other) + : basic_handle( + ASIO_MOVE_CAST(basic_random_access_handle)(other)) + { + } + + /// Move-assign a basic_random_access_handle from another. + /** + * This assignment operator moves a random-access handle from one object to + * another. + * + * @param other The other basic_random_access_handle object from which the + * move will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_random_access_handle(io_context&) + * constructor. + */ + basic_random_access_handle& operator=(basic_random_access_handle&& other) + { + basic_handle::operator=( + ASIO_MOVE_CAST(basic_random_access_handle)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Write some data to the handle at the specified offset. + /** + * This function is used to write data to the random-access handle. The + * function call will block until one or more bytes of the data has been + * written successfully, or until an error occurs. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some_at operation may not write all of the data. Consider + * using the @ref write_at function if you need to ensure that all data is + * written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.write_some_at(42, asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some_at(uint64_t offset, + const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some_at( + this->get_implementation(), offset, buffers, ec); + asio::detail::throw_error(ec, "write_some_at"); + return s; + } + + /// Write some data to the handle at the specified offset. + /** + * This function is used to write data to the random-access handle. The + * function call will block until one or more bytes of the data has been + * written successfully, or until an error occurs. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write_at function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some_at(uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return this->get_service().write_some_at( + this->get_implementation(), offset, buffers, ec); + } + + /// Start an asynchronous write at the specified offset. + /** + * This function is used to asynchronously write data to the random-access + * handle. The function call always returns immediately. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write_at function if you need to ensure that + * all data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_write_some_at(42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some_at(uint64_t offset, + const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + return this->get_service().async_write_some_at(this->get_implementation(), + offset, buffers, ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Read some data from the handle at the specified offset. + /** + * This function is used to read data from the random-access handle. The + * function call will block until one or more bytes of data has been read + * successfully, or until an error occurs. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read_at function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.read_some_at(42, asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some_at(uint64_t offset, + const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some_at( + this->get_implementation(), offset, buffers, ec); + asio::detail::throw_error(ec, "read_some_at"); + return s; + } + + /// Read some data from the handle at the specified offset. + /** + * This function is used to read data from the random-access handle. The + * function call will block until one or more bytes of data has been read + * successfully, or until an error occurs. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read_at function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some_at(uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return this->get_service().read_some_at( + this->get_implementation(), offset, buffers, ec); + } + + /// Start an asynchronous read at the specified offset. + /** + * This function is used to asynchronously read data from the random-access + * handle. The function call always returns immediately. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read_at function if you need to ensure that + * the requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_read_some_at(42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some_at(uint64_t offset, + const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + return this->get_service().async_read_some_at(this->get_implementation(), + offset, buffers, ASIO_MOVE_CAST(ReadHandler)(handler)); + } +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/basic_stream_handle.hpp b/subprojects/asio/include/asio/windows/basic_stream_handle.hpp new file mode 100644 index 000000000..3bfce6884 --- /dev/null +++ b/subprojects/asio/include/asio/windows/basic_stream_handle.hpp @@ -0,0 +1,359 @@ +// +// windows/basic_stream_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP +#define ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/windows/basic_handle.hpp" +#include "asio/windows/stream_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Provides stream-oriented handle functionality. +/** + * The windows::basic_stream_handle class template provides asynchronous and + * blocking stream-oriented handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class basic_stream_handle + : public basic_handle +{ +public: + /// The native representation of a handle. + typedef typename StreamHandleService::native_handle_type native_handle_type; + + /// Construct a basic_stream_handle without opening it. + /** + * This constructor creates a stream handle without opening it. The handle + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param io_context The io_context object that the stream handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit basic_stream_handle(asio::io_context& io_context) + : basic_handle(io_context) + { + } + + /// Construct a basic_stream_handle on an existing native handle. + /** + * This constructor creates a stream handle object to hold an existing native + * handle. + * + * @param io_context The io_context object that the stream handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_handle(asio::io_context& io_context, + const native_handle_type& handle) + : basic_handle(io_context, handle) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_stream_handle from another. + /** + * This constructor moves a stream handle from one object to another. + * + * @param other The other basic_stream_handle object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_handle(io_context&) constructor. + */ + basic_stream_handle(basic_stream_handle&& other) + : basic_handle( + ASIO_MOVE_CAST(basic_stream_handle)(other)) + { + } + + /// Move-assign a basic_stream_handle from another. + /** + * This assignment operator moves a stream handle from one object to + * another. + * + * @param other The other basic_stream_handle object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_handle(io_context&) constructor. + */ + basic_stream_handle& operator=(basic_stream_handle&& other) + { + basic_handle::operator=( + ASIO_MOVE_CAST(basic_stream_handle)(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Write some data to the handle. + /** + * This function is used to write data to the stream handle. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the handle. + /** + * This function is used to write data to the stream handle. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().write_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream handle. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the handle. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + return this->get_service().async_write_some(this->get_implementation(), + buffers, ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Read some data from the handle. + /** + * This function is used to read data from the stream handle. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the handle. + /** + * This function is used to read data from the stream handle. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().read_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream handle. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + return this->get_service().async_read_some(this->get_implementation(), + buffers, ASIO_MOVE_CAST(ReadHandler)(handler)); + } +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/object_handle.hpp b/subprojects/asio/include/asio/windows/object_handle.hpp new file mode 100644 index 000000000..581b56800 --- /dev/null +++ b/subprojects/asio/include/asio/windows/object_handle.hpp @@ -0,0 +1,381 @@ +// +// windows/object_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2011 Boris Schaeling (boris@highscore.de) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_OBJECT_HANDLE_HPP +#define ASIO_WINDOWS_OBJECT_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/async_result.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_object_handle_service.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/windows/basic_object_handle.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#define ASIO_SVC_T asio::detail::win_object_handle_service + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +#if defined(ASIO_ENABLE_OLD_SERVICES) +// Typedef for the typical usage of an object handle. +typedef basic_object_handle<> object_handle; +#else // defined(ASIO_ENABLE_OLD_SERVICES) +/// Provides object-oriented handle functionality. +/** + * The windows::object_handle class provides asynchronous and blocking + * object-oriented handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class object_handle + : ASIO_SVC_ACCESS basic_io_object +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The native representation of a handle. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef ASIO_SVC_T::native_handle_type native_handle_type; +#endif + + /// An object_handle is always the lowest layer. + typedef object_handle lowest_layer_type; + + /// Construct an object_handle without opening it. + /** + * This constructor creates an object handle without opening it. + * + * @param io_context The io_context object that the object handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit object_handle(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct an object_handle on an existing native handle. + /** + * This constructor creates an object handle object to hold an existing native + * handle. + * + * @param io_context The io_context object that the object handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param native_handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + object_handle(asio::io_context& io_context, + const native_handle_type& native_handle) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), native_handle, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct an object_handle from another. + /** + * This constructor moves an object handle from one object to another. + * + * @param other The other object_handle object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c object_handle(io_context&) constructor. + */ + object_handle(object_handle&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign an object_handle from another. + /** + * This assignment operator moves an object handle from one object to another. + * + * @param other The other object_handle object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c object_handle(io_context&) constructor. + */ + object_handle& operator=(object_handle&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since an object_handle cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since an object_handle cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param handle A native handle. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& handle) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), handle, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param handle A native handle. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& handle, + asio::error_code& ec) + { + this->get_service().assign(this->get_implementation(), handle, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the handle is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native handle representation. + /** + * This function may be used to obtain the underlying representation of the + * handle. This is intended to allow access to native handle functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform a blocking wait on the object handle. + /** + * This function is used to wait for the object handle to be set to the + * signalled state. This function blocks and does not return until the object + * handle has been set to the signalled state. + * + * @throws asio::system_error Thrown on failure. + */ + void wait() + { + asio::error_code ec; + this->get_service().wait(this->get_implementation(), ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Perform a blocking wait on the object handle. + /** + * This function is used to wait for the object handle to be set to the + * signalled state. This function blocks and does not return until the object + * handle has been set to the signalled state. + * + * @param ec Set to indicate what error occurred, if any. + */ + void wait(asio::error_code& ec) + { + this->get_service().wait(this->get_implementation(), ec); + } + + /// Start an asynchronous wait on the object handle. + /** + * This function is be used to initiate an asynchronous wait against the + * object handle. It always returns immediately. + * + * @param handler The handler to be called when the object handle is set to + * the signalled state. Copies will be made of the handler as required. The + * function signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(ASIO_MOVE_ARG(WaitHandler) handler) + { + asio::async_completion init(handler); + + this->get_service().async_wait(this->get_implementation(), + init.completion_handler); + + return init.result.get(); + } +}; +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#undef ASIO_SVC_T + +#endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_WINDOWS_OBJECT_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/object_handle_service.hpp b/subprojects/asio/include/asio/windows/object_handle_service.hpp new file mode 100644 index 000000000..95436d790 --- /dev/null +++ b/subprojects/asio/include/asio/windows/object_handle_service.hpp @@ -0,0 +1,183 @@ +// +// windows/object_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2011 Boris Schaeling (boris@highscore.de) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_OBJECT_HANDLE_SERVICE_HPP +#define ASIO_WINDOWS_OBJECT_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/async_result.hpp" +#include "asio/detail/win_object_handle_service.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Default service implementation for an object handle. +class object_handle_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + +private: + // The type of the platform-specific implementation. + typedef detail::win_object_handle_service service_impl_type; + +public: + /// The type of an object handle implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native handle type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new object handle service for the specified io_context. + explicit object_handle_service(asio::io_context& io_context) + : asio::detail::service_base(io_context), + service_impl_(io_context) + { + } + + /// Construct a new object handle implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new object handle implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another object handle implementation. + void move_assign(implementation_type& impl, + object_handle_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy an object handle implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Assign an existing native handle to an object handle. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) + { + service_impl_.assign(impl, handle, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close an object handle implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native handle implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the handle. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + // Wait for a signaled state. + void wait(implementation_type& impl, asio::error_code& ec) + { + service_impl_.wait(impl, ec); + } + + /// Start an asynchronous wait. + template + ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(implementation_type& impl, + ASIO_MOVE_ARG(WaitHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_wait(impl, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_OBJECT_HANDLE_SERVICE_HPP diff --git a/subprojects/asio/include/asio/windows/overlapped_handle.hpp b/subprojects/asio/include/asio/windows/overlapped_handle.hpp new file mode 100644 index 000000000..3d479ba6c --- /dev/null +++ b/subprojects/asio/include/asio/windows/overlapped_handle.hpp @@ -0,0 +1,331 @@ +// +// windows/overlapped_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_OVERLAPPED_HANDLE_HPP +#define ASIO_WINDOWS_OVERLAPPED_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/async_result.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#define ASIO_SVC_T asio::detail::win_iocp_handle_service + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Provides Windows handle functionality for objects that support +/// overlapped I/O. +/** + * The windows::overlapped_handle class provides the ability to wrap a Windows + * handle. The underlying object referred to by the handle must support + * overlapped I/O. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class overlapped_handle + : ASIO_SVC_ACCESS basic_io_object +{ +public: + /// The type of the executor associated with the object. + typedef io_context::executor_type executor_type; + + /// The native representation of a handle. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef ASIO_SVC_T::native_handle_type native_handle_type; +#endif + + /// An overlapped_handle is always the lowest layer. + typedef overlapped_handle lowest_layer_type; + + /// Construct an overlapped_handle without opening it. + /** + * This constructor creates a handle without opening it. + * + * @param io_context The io_context object that the handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit overlapped_handle(asio::io_context& io_context) + : basic_io_object(io_context) + { + } + + /// Construct an overlapped_handle on an existing native handle. + /** + * This constructor creates a handle object to hold an existing native handle. + * + * @param io_context The io_context object that the handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param handle A native handle. + * + * @throws asio::system_error Thrown on failure. + */ + overlapped_handle(asio::io_context& io_context, + const native_handle_type& handle) + : basic_io_object(io_context) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), handle, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct an overlapped_handle from another. + /** + * This constructor moves a handle from one object to another. + * + * @param other The other overlapped_handle object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c overlapped_handle(io_context&) constructor. + */ + overlapped_handle(overlapped_handle&& other) + : basic_io_object(std::move(other)) + { + } + + /// Move-assign an overlapped_handle from another. + /** + * This assignment operator moves a handle from one object to another. + * + * @param other The other overlapped_handle object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c overlapped_handle(io_context&) constructor. + */ + overlapped_handle& operator=(overlapped_handle&& other) + { + basic_io_object::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return basic_io_object::get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return basic_io_object::get_io_service(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return basic_io_object::get_executor(); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since an overlapped_handle cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since an overlapped_handle cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param handle A native handle. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& handle) + { + asio::error_code ec; + this->get_service().assign(this->get_implementation(), handle, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param handle A native handle. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& handle, + asio::error_code& ec) + { + this->get_service().assign(this->get_implementation(), handle, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the handle is open. + bool is_open() const + { + return this->get_service().is_open(this->get_implementation()); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->get_service().close(this->get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + this->get_service().close(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native handle representation. + /** + * This function may be used to obtain the underlying representation of the + * handle. This is intended to allow access to native handle functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return this->get_service().native_handle(this->get_implementation()); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->get_service().cancel(this->get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + this->get_service().cancel(this->get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + +protected: + /// Protected destructor to prevent deletion through this type. + /** + * This function destroys the handle, cancelling any outstanding asynchronous + * wait operations associated with the handle as if by calling @c cancel. + */ + ~overlapped_handle() + { + } +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#undef ASIO_SVC_T + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // !defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_OVERLAPPED_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/overlapped_ptr.hpp b/subprojects/asio/include/asio/windows/overlapped_ptr.hpp new file mode 100644 index 000000000..ce0b2a42d --- /dev/null +++ b/subprojects/asio/include/asio/windows/overlapped_ptr.hpp @@ -0,0 +1,116 @@ +// +// windows/overlapped_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_OVERLAPPED_PTR_HPP +#define ASIO_WINDOWS_OVERLAPPED_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/win_iocp_overlapped_ptr.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Wraps a handler to create an OVERLAPPED object for use with overlapped I/O. +/** + * A special-purpose smart pointer used to wrap an application handler so that + * it can be passed as the LPOVERLAPPED argument to overlapped I/O functions. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class overlapped_ptr + : private noncopyable +{ +public: + /// Construct an empty overlapped_ptr. + overlapped_ptr() + : impl_() + { + } + + /// Construct an overlapped_ptr to contain the specified handler. + template + explicit overlapped_ptr(asio::io_context& io_context, + ASIO_MOVE_ARG(Handler) handler) + : impl_(io_context, ASIO_MOVE_CAST(Handler)(handler)) + { + } + + /// Destructor automatically frees the OVERLAPPED object unless released. + ~overlapped_ptr() + { + } + + /// Reset to empty. + void reset() + { + impl_.reset(); + } + + /// Reset to contain the specified handler, freeing any current OVERLAPPED + /// object. + template + void reset(asio::io_context& io_context, + ASIO_MOVE_ARG(Handler) handler) + { + impl_.reset(io_context, ASIO_MOVE_CAST(Handler)(handler)); + } + + /// Get the contained OVERLAPPED object. + OVERLAPPED* get() + { + return impl_.get(); + } + + /// Get the contained OVERLAPPED object. + const OVERLAPPED* get() const + { + return impl_.get(); + } + + /// Release ownership of the OVERLAPPED object. + OVERLAPPED* release() + { + return impl_.release(); + } + + /// Post completion notification for overlapped operation. Releases ownership. + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + impl_.complete(ec, bytes_transferred); + } + +private: + detail::win_iocp_overlapped_ptr impl_; +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_WINDOWS_OVERLAPPED_PTR_HPP diff --git a/subprojects/asio/include/asio/windows/random_access_handle.hpp b/subprojects/asio/include/asio/windows/random_access_handle.hpp new file mode 100644 index 000000000..301d5f8dd --- /dev/null +++ b/subprojects/asio/include/asio/windows/random_access_handle.hpp @@ -0,0 +1,378 @@ +// +// windows/random_access_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP +#define ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/windows/overlapped_handle.hpp" + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/windows/basic_random_access_handle.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +#if defined(ASIO_ENABLE_OLD_SERVICES) +// Typedef for the typical usage of a random-access handle. +typedef basic_random_access_handle<> random_access_handle; +#else // defined(ASIO_ENABLE_OLD_SERVICES) +/// Provides random-access handle functionality. +/** + * The windows::random_access_handle class provides asynchronous and + * blocking random-access handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class random_access_handle + : public overlapped_handle +{ +public: + /// Construct a random_access_handle without opening it. + /** + * This constructor creates a random-access handle without opening it. The + * handle needs to be opened before data can be written to or read from it. + * + * @param io_context The io_context object that the random-access handle will + * use to dispatch handlers for any asynchronous operations performed on the + * handle. + */ + explicit random_access_handle(asio::io_context& io_context) + : overlapped_handle(io_context) + { + } + + /// Construct a random_access_handle on an existing native handle. + /** + * This constructor creates a random-access handle object to hold an existing + * native handle. + * + * @param io_context The io_context object that the random-access handle will + * use to dispatch handlers for any asynchronous operations performed on the + * handle. + * + * @param handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + random_access_handle(asio::io_context& io_context, + const native_handle_type& handle) + : overlapped_handle(io_context, handle) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a random_access_handle from another. + /** + * This constructor moves a random-access handle from one object to another. + * + * @param other The other random_access_handle object from which the + * move will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c random_access_handle(io_context&) + * constructor. + */ + random_access_handle(random_access_handle&& other) + : overlapped_handle(std::move(other)) + { + } + + /// Move-assign a random_access_handle from another. + /** + * This assignment operator moves a random-access handle from one object to + * another. + * + * @param other The other random_access_handle object from which the + * move will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c random_access_handle(io_context&) + * constructor. + */ + random_access_handle& operator=(random_access_handle&& other) + { + overlapped_handle::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Write some data to the handle at the specified offset. + /** + * This function is used to write data to the random-access handle. The + * function call will block until one or more bytes of the data has been + * written successfully, or until an error occurs. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some_at operation may not write all of the data. Consider + * using the @ref write_at function if you need to ensure that all data is + * written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.write_some_at(42, asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some_at(uint64_t offset, + const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some_at( + this->get_implementation(), offset, buffers, ec); + asio::detail::throw_error(ec, "write_some_at"); + return s; + } + + /// Write some data to the handle at the specified offset. + /** + * This function is used to write data to the random-access handle. The + * function call will block until one or more bytes of the data has been + * written successfully, or until an error occurs. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write_at function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some_at(uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return this->get_service().write_some_at( + this->get_implementation(), offset, buffers, ec); + } + + /// Start an asynchronous write at the specified offset. + /** + * This function is used to asynchronously write data to the random-access + * handle. The function call always returns immediately. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write_at function if you need to ensure that + * all data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_write_some_at(42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some_at(uint64_t offset, + const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + asio::async_completion init(handler); + + this->get_service().async_write_some_at(this->get_implementation(), + offset, buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the handle at the specified offset. + /** + * This function is used to read data from the random-access handle. The + * function call will block until one or more bytes of data has been read + * successfully, or until an error occurs. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read_at function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.read_some_at(42, asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some_at(uint64_t offset, + const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some_at( + this->get_implementation(), offset, buffers, ec); + asio::detail::throw_error(ec, "read_some_at"); + return s; + } + + /// Read some data from the handle at the specified offset. + /** + * This function is used to read data from the random-access handle. The + * function call will block until one or more bytes of data has been read + * successfully, or until an error occurs. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read_at function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some_at(uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return this->get_service().read_some_at( + this->get_implementation(), offset, buffers, ec); + } + + /// Start an asynchronous read at the specified offset. + /** + * This function is used to asynchronously read data from the random-access + * handle. The function call always returns immediately. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read_at function if you need to ensure that + * the requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_read_some_at(42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some_at(uint64_t offset, + const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + asio::async_completion init(handler); + + this->get_service().async_read_some_at(this->get_implementation(), + offset, buffers, init.completion_handler); + + return init.result.get(); + } +}; +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/random_access_handle_service.hpp b/subprojects/asio/include/asio/windows/random_access_handle_service.hpp new file mode 100644 index 000000000..ebccf3e39 --- /dev/null +++ b/subprojects/asio/include/asio/windows/random_access_handle_service.hpp @@ -0,0 +1,214 @@ +// +// windows/random_access_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP +#define ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Default service implementation for a random-access handle. +class random_access_handle_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + +private: + // The type of the platform-specific implementation. + typedef detail::win_iocp_handle_service service_impl_type; + +public: + /// The type of a random-access handle implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native handle type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new random-access handle service for the specified io_context. + explicit random_access_handle_service(asio::io_context& io_context) + : asio::detail::service_base< + random_access_handle_service>(io_context), + service_impl_(io_context) + { + } + + /// Construct a new random-access handle implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new random-access handle implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another random-access handle implementation. + void move_assign(implementation_type& impl, + random_access_handle_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a random-access handle implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Assign an existing native handle to a random-access handle. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) + { + service_impl_.assign(impl, handle, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a random-access handle implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native handle implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the handle. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write the given data at the specified offset. + template + std::size_t write_some_at(implementation_type& impl, uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some_at(impl, offset, buffers, ec); + } + + /// Start an asynchronous write at the specified offset. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some_at(implementation_type& impl, + uint64_t offset, const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_write_some_at(impl, + offset, buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the specified offset. + template + std::size_t read_some_at(implementation_type& impl, uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some_at(impl, offset, buffers, ec); + } + + /// Start an asynchronous read at the specified offset. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some_at(implementation_type& impl, + uint64_t offset, const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_read_some_at(impl, + offset, buffers, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP diff --git a/subprojects/asio/include/asio/windows/stream_handle.hpp b/subprojects/asio/include/asio/windows/stream_handle.hpp new file mode 100644 index 000000000..8f0a21b1f --- /dev/null +++ b/subprojects/asio/include/asio/windows/stream_handle.hpp @@ -0,0 +1,362 @@ +// +// windows/stream_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_STREAM_HANDLE_HPP +#define ASIO_WINDOWS_STREAM_HANDLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/windows/overlapped_handle.hpp" + +#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_ENABLE_OLD_SERVICES) +# include "asio/windows/basic_stream_handle.hpp" +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +#if defined(ASIO_ENABLE_OLD_SERVICES) +// Typedef for the typical usage of a stream-oriented handle. +typedef basic_stream_handle<> stream_handle; +#else // defined(ASIO_ENABLE_OLD_SERVICES) +/// Provides stream-oriented handle functionality. +/** + * The windows::stream_handle class provides asynchronous and blocking + * stream-oriented handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +class stream_handle + : public overlapped_handle +{ +public: + /// Construct a stream_handle without opening it. + /** + * This constructor creates a stream handle without opening it. The handle + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param io_context The io_context object that the stream handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit stream_handle(asio::io_context& io_context) + : overlapped_handle(io_context) + { + } + + /// Construct a stream_handle on an existing native handle. + /** + * This constructor creates a stream handle object to hold an existing native + * handle. + * + * @param io_context The io_context object that the stream handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + stream_handle(asio::io_context& io_context, + const native_handle_type& handle) + : overlapped_handle(io_context, handle) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a stream_handle from another. + /** + * This constructor moves a stream handle from one object to another. + * + * @param other The other stream_handle object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c stream_handle(io_context&) constructor. + */ + stream_handle(stream_handle&& other) + : overlapped_handle(std::move(other)) + { + } + + /// Move-assign a stream_handle from another. + /** + * This assignment operator moves a stream handle from one object to + * another. + * + * @param other The other stream_handle object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c stream_handle(io_context&) constructor. + */ + stream_handle& operator=(stream_handle&& other) + { + overlapped_handle::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Write some data to the handle. + /** + * This function is used to write data to the stream handle. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().write_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the handle. + /** + * This function is used to write data to the stream handle. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().write_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream handle. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the handle. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + asio::async_completion init(handler); + + this->get_service().async_write_some( + this->get_implementation(), buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the handle. + /** + * This function is used to read data from the stream handle. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->get_service().read_some( + this->get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the handle. + /** + * This function is used to read data from the stream handle. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->get_service().read_some( + this->get_implementation(), buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream handle. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + asio::async_completion init(handler); + + this->get_service().async_read_some( + this->get_implementation(), buffers, init.completion_handler); + + return init.result.get(); + } +}; +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_WINDOWS_STREAM_HANDLE_HPP diff --git a/subprojects/asio/include/asio/windows/stream_handle_service.hpp b/subprojects/asio/include/asio/windows/stream_handle_service.hpp new file mode 100644 index 000000000..1c665d5e1 --- /dev/null +++ b/subprojects/asio/include/asio/windows/stream_handle_service.hpp @@ -0,0 +1,210 @@ +// +// windows/stream_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP +#define ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_ENABLE_OLD_SERVICES) + +#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/async_result.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" +#include "asio/error.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace windows { + +/// Default service implementation for a stream handle. +class stream_handle_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_context::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_context::id id; +#endif + +private: + // The type of the platform-specific implementation. + typedef detail::win_iocp_handle_service service_impl_type; + +public: + /// The type of a stream handle implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native handle type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef service_impl_type::native_handle_type native_handle_type; +#endif + + /// Construct a new stream handle service for the specified io_context. + explicit stream_handle_service(asio::io_context& io_context) + : asio::detail::service_base(io_context), + service_impl_(io_context) + { + } + + /// Construct a new stream handle implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a new stream handle implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + service_impl_.move_construct(impl, other_impl); + } + + /// Move-assign from another stream handle implementation. + void move_assign(implementation_type& impl, + stream_handle_service& other_service, + implementation_type& other_impl) + { + service_impl_.move_assign(impl, other_service.service_impl_, other_impl); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroy a stream handle implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Assign an existing native handle to a stream handle. + ASIO_SYNC_OP_VOID assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) + { + service_impl_.assign(impl, handle, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a stream handle implementation. + ASIO_SYNC_OP_VOID close(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.close(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native handle implementation. + native_handle_type native_handle(implementation_type& impl) + { + return service_impl_.native_handle(impl); + } + + /// Cancel all asynchronous operations associated with the handle. + ASIO_SYNC_OP_VOID cancel(implementation_type& impl, + asio::error_code& ec) + { + service_impl_.cancel(impl, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write the given data to the stream. + template + std::size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some(impl, buffers, ec); + } + + /// Start an asynchronous write. + template + ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_write_some(impl, buffers, init.completion_handler); + + return init.result.get(); + } + + /// Read some data from the stream. + template + std::size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some(impl, buffers, ec); + } + + /// Start an asynchronous read. + template + ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler) + { + asio::async_completion init(handler); + + service_impl_.async_read_some(impl, buffers, init.completion_handler); + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + service_impl_.shutdown(); + } + + // The platform-specific implementation. + service_impl_type service_impl_; +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_ENABLE_OLD_SERVICES) + +#endif // ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP diff --git a/subprojects/asio/include/asio/write.hpp b/subprojects/asio/include/asio/write.hpp new file mode 100644 index 000000000..7f9251a87 --- /dev/null +++ b/subprojects/asio/include/asio/write.hpp @@ -0,0 +1,928 @@ +// +// write.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WRITE_HPP +#define ASIO_WRITE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/buffer.hpp" +#include "asio/error.hpp" + +#if !defined(ASIO_NO_EXTENSIONS) +# include "asio/basic_streambuf_fwd.hpp" +#endif // !defined(ASIO_NO_EXTENSIONS) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/** + * @defgroup write asio::write + * + * @brief The @c write function is a composed operation that writes a certain + * amount of data to a stream before returning. + */ +/*@{*/ + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write(s, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + typename enable_if< + is_const_buffer_sequence::value + >::type* = 0); + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write(s, asio::buffer(data, size), ec); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, buffers, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + asio::error_code& ec, + typename enable_if< + is_const_buffer_sequence::value + >::type* = 0); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's write_some function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write(s, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, + typename enable_if< + is_const_buffer_sequence::value + >::type* = 0); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's write_some function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_const_buffer_sequence::value + >::type* = 0); + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied dynamic buffer sequence has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers The dynamic buffer sequence from which data will be written. + * Successfully written data is automatically consumed from the buffers. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied dynamic buffer sequence has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers The dynamic buffer sequence from which data will be written. + * Successfully written data is automatically consumed from the buffers. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, buffers, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied dynamic buffer sequence has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers The dynamic buffer sequence from which data will be written. + * Successfully written data is automatically consumed from the buffers. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's write_some function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied dynamic buffer sequence has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers The dynamic buffer sequence from which data will be written. + * Successfully written data is automatically consumed from the buffers. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's write_some function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write(SyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, asio::error_code& ec, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t write(SyncWriteStream& s, basic_streambuf& b); + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, b, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t write(SyncWriteStream& s, basic_streambuf& b, + asio::error_code& ec); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's write_some function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t write(SyncWriteStream& s, basic_streambuf& b, + CompletionCondition completion_condition); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's write_some function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write(SyncWriteStream& s, basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ +/** + * @defgroup async_write asio::async_write + * + * @brief The @c async_write function is a composed asynchronous operation that + * writes a certain amount of data to a stream before completion. + */ +/*@{*/ + +/// Start an asynchronous operation to write all of the supplied data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_write_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other write operations (such + * as async_write, the stream's async_write_some function, or any other composed + * operations that perform writes) until this operation completes. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_write(s, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_const_buffer_sequence::value + >::type* = 0); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_write_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other write operations (such + * as async_write, the stream's async_write_some function, or any other composed + * operations that perform writes) until this operation completes. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's async_write_some function. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::async_write(s, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_const_buffer_sequence::value + >::type* = 0); + +/// Start an asynchronous operation to write all of the supplied data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied dynamic buffer sequence has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_write_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other write operations (such + * as async_write, the stream's async_write_some function, or any other composed + * operations that perform writes) until this operation completes. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param buffers The dynamic buffer sequence from which data will be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. Successfully written + * data is automatically consumed from the buffers. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied dynamic buffer sequence has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_write_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other write operations (such + * as async_write, the stream's async_write_some function, or any other composed + * operations that perform writes) until this operation completes. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param buffers The dynamic buffer sequence from which data will be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. Successfully written + * data is automatically consumed from the buffers. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's async_write_some function. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, + ASIO_MOVE_ARG(DynamicBuffer) buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler, + typename enable_if< + is_dynamic_buffer::type>::value + >::type* = 0); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Start an asynchronous operation to write all of the supplied data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_write_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other write operations (such + * as async_write, the stream's async_write_some function, or any other composed + * operations that perform writes) until this operation completes. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, basic_streambuf& b, + ASIO_MOVE_ARG(WriteHandler) handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_write_some function, and is known as a composed operation. The + * program must ensure that the stream performs no other write operations (such + * as async_write, the stream's async_write_some function, or any other composed + * operations that perform writes) until this operation completes. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_write_some operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the stream's async_write_some function. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write(AsyncWriteStream& s, basic_streambuf& b, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/write.hpp" + +#endif // ASIO_WRITE_HPP diff --git a/subprojects/asio/include/asio/write_at.hpp b/subprojects/asio/include/asio/write_at.hpp new file mode 100644 index 000000000..b7e5da482 --- /dev/null +++ b/subprojects/asio/include/asio/write_at.hpp @@ -0,0 +1,679 @@ +// +// write_at.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WRITE_AT_HPP +#define ASIO_WRITE_AT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/error.hpp" + +#if !defined(ASIO_NO_EXTENSIONS) +# include "asio/basic_streambuf_fwd.hpp" +#endif // !defined(ASIO_NO_EXTENSIONS) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/** + * @defgroup write_at asio::write_at + * + * @brief The @c write_at function is a composed operation that writes a + * certain amount of data at a specified offset before returning. + */ +/*@{*/ + +/// Write all of the supplied data at the specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * device. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write_at(d, 42, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write_at( + * d, offset, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers); + +/// Write all of the supplied data at the specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * device. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write_at(d, 42, + * asio::buffer(data, size), ec); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write_at( + * d, offset, buffers, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + asio::error_code& ec); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the device's write_some_at function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write_at(d, 42, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the device's write_some_at function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Write all of the supplied data at the specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b The basic_streambuf object from which data will be written. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::write_at( + * d, 42, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, basic_streambuf& b); + +/// Write all of the supplied data at the specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes transferred. + * + * @note This overload is equivalent to calling: + * @code asio::write_at( + * d, 42, b, + * asio::transfer_all(), ec); @endcode + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + uint64_t offset, basic_streambuf& b, + asio::error_code& ec); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the device's write_some_at function. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, uint64_t offset, + basic_streambuf& b, CompletionCondition completion_condition); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the device's write_some_at function. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, uint64_t offset, + basic_streambuf& b, CompletionCondition completion_condition, + asio::error_code& ec); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ +/** + * @defgroup async_write_at asio::async_write_at + * + * @brief The @c async_write_at function is a composed asynchronous operation + * that writes a certain amount of data at the specified offset before + * completion. + */ +/*@{*/ + +/// Start an asynchronous operation to write all of the supplied data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * async_write_some_at function, and is known as a composed operation. + * The program must ensure that the device performs no overlapping + * write operations (such as async_write_at, the device's async_write_some_at + * function, or any other composed operations that perform writes) until this + * operation completes. Operations are overlapping if the regions defined by + * their offsets, and the numbers of bytes to write, intersect. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_write_at(d, 42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, uint64_t offset, + const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler); + +/// Start an asynchronous operation to write a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * async_write_some_at function, and is known as a composed operation. + * The program must ensure that the device performs no overlapping + * write operations (such as async_write_at, the device's async_write_some_at + * function, or any other composed operations that perform writes) until this + * operation completes. Operations are overlapping if the regions defined by + * their offsets, and the numbers of bytes to write, intersect. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the device's async_write_some_at function. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::async_write_at(d, 42, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, + uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler); + +#if !defined(ASIO_NO_EXTENSIONS) +#if !defined(ASIO_NO_IOSTREAM) + +/// Start an asynchronous operation to write all of the supplied data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the device's + * async_write_some_at function, and is known as a composed operation. + * The program must ensure that the device performs no overlapping + * write operations (such as async_write_at, the device's async_write_some_at + * function, or any other composed operations that perform writes) until this + * operation completes. Operations are overlapping if the regions defined by + * their offsets, and the numbers of bytes to write, intersect. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, uint64_t offset, + basic_streambuf& b, ASIO_MOVE_ARG(WriteHandler) handler); + +/// Start an asynchronous operation to write a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns 0. + * + * This operation is implemented in terms of zero or more calls to the device's + * async_write_some_at function, and is known as a composed operation. + * The program must ensure that the device performs no overlapping + * write operations (such as async_write_at, the device's async_write_some_at + * function, or any other composed operations that perform writes) until this + * operation completes. Operations are overlapping if the regions defined by + * their offsets, and the numbers of bytes to write, intersect. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code std::size_t completion_condition( + * // Result of latest async_write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of 0 indicates that the write operation is complete. A + * non-zero return value indicates the maximum number of bytes to be written on + * the next call to the device's async_write_some_at function. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_context::post(). + */ +template +ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) +async_write_at(AsyncRandomAccessWriteDevice& d, uint64_t offset, + basic_streambuf& b, CompletionCondition completion_condition, + ASIO_MOVE_ARG(WriteHandler) handler); + +#endif // !defined(ASIO_NO_IOSTREAM) +#endif // !defined(ASIO_NO_EXTENSIONS) + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/write_at.hpp" + +#endif // ASIO_WRITE_AT_HPP diff --git a/subprojects/asio/include/asio/yield.hpp b/subprojects/asio/include/asio/yield.hpp new file mode 100644 index 000000000..b527ac9b4 --- /dev/null +++ b/subprojects/asio/include/asio/yield.hpp @@ -0,0 +1,23 @@ +// +// yield.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "coroutine.hpp" + +#ifndef reenter +# define reenter(c) ASIO_CORO_REENTER(c) +#endif + +#ifndef yield +# define yield ASIO_CORO_YIELD +#endif + +#ifndef fork +# define fork ASIO_CORO_FORK +#endif diff --git a/subprojects/valijson/Authors b/subprojects/valijson/Authors new file mode 100644 index 000000000..d2c0206d3 --- /dev/null +++ b/subprojects/valijson/Authors @@ -0,0 +1,38 @@ +Tristan Penman, tristan@tristanpenman.com + Core library development + +James Jensen, jjensen@akamai.com, changes Copyright (c) 2016 Akamai Technologies + Polymorphic constraint support + +Tengiz Sharafiev, btolfa+github@gmail.com + Adapter for nlohmann/json parser library + +hotwatermorning (github username), hotwatermorning@gmail.com + Adapter for json11 parser library + +shsfre09 (github username), + Adapter for picojson parser library + +Michael Smith, michael.smith@puppetlabs.com + Memory management improvements for RapidJson parser library + +Richard Clamp, richardc@unixbeard.net + Boost-related fixes + +Lars Immisch, lars@ibp.de + noboost branch + +Nicolas Witczak, n_witczak@yahoo.com + C++11 improvements + +Martin Robinson, 0x4d52@gmail.com + Bug fixes + +Pierre Lamot, pierre.lamot@yahoo.fr + Adapter for Qt JSON parser + +drewxa (github username), bo.ro.da@mail.ru + Adapter for Poco JSON parser + +Jordan Bayles (jophba), jophba@chromium.org + JsonCpp owner diff --git a/subprojects/valijson/LICENSE b/subprojects/valijson/LICENSE new file mode 100644 index 000000000..77d38164c --- /dev/null +++ b/subprojects/valijson/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2016, Tristan Penman +Copyright (c) 2016, Akamai Technolgies, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/subprojects/valijson/include/compat/optional.hpp b/subprojects/valijson/include/compat/optional.hpp new file mode 100644 index 000000000..b0182dfbf --- /dev/null +++ b/subprojects/valijson/include/compat/optional.hpp @@ -0,0 +1,1043 @@ +// Copyright (C) 2011 - 2012 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// The idea and interface is based on Boost.Optional library +// authored by Fernando Luis Cacciola Carballal + +# ifndef ___OPTIONAL_HPP___ +# define ___OPTIONAL_HPP___ + +# include +# include +# include +# include +# include +# include +# include +# include + +# define TR2_OPTIONAL_REQUIRES(...) typename enable_if<__VA_ARGS__::value, bool>::type = false + +# if defined __GNUC__ // NOTE: GNUC is also defined for Clang +# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8) +# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ +# elif (__GNUC__ > 4) +# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ +# endif +# +# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7) +# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ +# elif (__GNUC__ > 4) +# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ +# endif +# +# if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1) +# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +# elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) +# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +# elif (__GNUC__ > 4) +# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +# endif +# endif +# +# if defined __clang_major__ +# if (__clang_major__ == 3 && __clang_minor__ >= 5) +# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ +# elif (__clang_major__ > 3) +# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ +# endif +# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ +# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ +# elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2) +# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ +# endif +# endif +# +# if defined _MSC_VER +# if (_MSC_VER >= 1900) +# define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ +# endif +# endif + +# if defined __clang__ +# if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9) +# define OPTIONAL_HAS_THIS_RVALUE_REFS 1 +# else +# define OPTIONAL_HAS_THIS_RVALUE_REFS 0 +# endif +# elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +# define OPTIONAL_HAS_THIS_RVALUE_REFS 1 +# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ +# define OPTIONAL_HAS_THIS_RVALUE_REFS 1 +# else +# define OPTIONAL_HAS_THIS_RVALUE_REFS 0 +# endif + + +# if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1 +# define OPTIONAL_CONSTEXPR_INIT_LIST constexpr +# else +# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0 +# define OPTIONAL_CONSTEXPR_INIT_LIST +# endif + +# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L) +# define OPTIONAL_HAS_MOVE_ACCESSORS 1 +# else +# define OPTIONAL_HAS_MOVE_ACCESSORS 0 +# endif + +# // In C++11 constexpr implies const, so we need to make non-const members also non-constexpr +# if (defined __cplusplus) && (__cplusplus == 201103L) +# define OPTIONAL_MUTABLE_CONSTEXPR +# else +# define OPTIONAL_MUTABLE_CONSTEXPR constexpr +# endif + +namespace std{ + + namespace experimental{ + + // BEGIN workaround for missing is_trivially_destructible +# if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ + // leave it: it is already there +# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ + // leave it: it is already there +# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ + // leave it: it is already there +# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS + // leave it: the user doesn't want it +# else + template + using is_trivially_destructible = std::has_trivial_destructor; +# endif + // END workaround for missing is_trivially_destructible + +# if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___) + // leave it; our metafunctions are already defined. +# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ + // leave it; our metafunctions are already defined. +# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ + // leave it: it is already there +# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS + // leave it: the user doesn't want it +# else + + + // workaround for missing traits in GCC and CLANG + template + struct is_nothrow_move_constructible + { + constexpr static bool value = std::is_nothrow_constructible::value; + }; + + + template + struct is_assignable + { + template + constexpr static bool has_assign(...) { return false; } + + template () = std::declval(), true)) > + // the comma operator is necessary for the cases where operator= returns void + constexpr static bool has_assign(bool) { return true; } + + constexpr static bool value = has_assign(true); + }; + + + template + struct is_nothrow_move_assignable + { + template + struct has_nothrow_move_assign { + constexpr static bool value = false; + }; + + template + struct has_nothrow_move_assign { + constexpr static bool value = noexcept( std::declval() = std::declval() ); + }; + + constexpr static bool value = has_nothrow_move_assign::value>::value; + }; + // end workaround + + +# endif + + + + // 20.5.4, optional for object types + template class optional; + + // 20.5.5, optional for lvalue reference types + template class optional; + + + // workaround: std utility functions aren't constexpr yet + template inline constexpr T&& constexpr_forward(typename std::remove_reference::type& t) noexcept + { + return static_cast(t); + } + + template inline constexpr T&& constexpr_forward(typename std::remove_reference::type&& t) noexcept + { + static_assert(!std::is_lvalue_reference::value, "!!"); + return static_cast(t); + } + + template inline constexpr typename std::remove_reference::type&& constexpr_move(T&& t) noexcept + { + return static_cast::type&&>(t); + } + + +#if defined NDEBUG +# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) +#else +# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{assert(!#CHECK);}(), (EXPR))) +#endif + + + namespace detail_ + { + + // static_addressof: a constexpr version of addressof + template + struct has_overloaded_addressof + { + template + constexpr static bool has_overload(...) { return false; } + + template ().operator&()) > + constexpr static bool has_overload(bool) { return true; } + + constexpr static bool value = has_overload(true); + }; + + template )> + constexpr T* static_addressof(T& ref) + { + return &ref; + } + + template )> + T* static_addressof(T& ref) + { + return std::addressof(ref); + } + + + // the call to convert(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A + template + constexpr U convert(U v) { return v; } + + } // namespace detail + + + constexpr struct trivial_init_t{} trivial_init{}; + + + // 20.5.6, In-place construction + constexpr struct in_place_t{} in_place{}; + + + // 20.5.7, Disengaged state indicator + struct nullopt_t + { + struct init{}; + constexpr explicit nullopt_t(init){} + }; + constexpr nullopt_t nullopt{nullopt_t::init()}; + + + // 20.5.8, class bad_optional_access + class bad_optional_access : public logic_error { + public: + explicit bad_optional_access(const string& what_arg) : logic_error{what_arg} {} + explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {} + }; + + + template + union storage_t + { + unsigned char dummy_; + T value_; + + constexpr storage_t( trivial_init_t ) noexcept : dummy_() {}; + + template + constexpr storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} + + ~storage_t(){} + }; + + + template + union constexpr_storage_t + { + unsigned char dummy_; + T value_; + + constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {}; + + template + constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} + + ~constexpr_storage_t() = default; + }; + + + template + struct optional_base + { + bool init_; + storage_t storage_; + + constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {}; + + explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {} + + explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} + + template explicit optional_base(in_place_t, Args&&... args) + : init_(true), storage_(constexpr_forward(args)...) {} + + template >)> + explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) + : init_(true), storage_(il, std::forward(args)...) {} + + ~optional_base() { if (init_) storage_.value_.T::~T(); } + }; + + + template + struct constexpr_optional_base + { + bool init_; + constexpr_storage_t storage_; + + constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {}; + + explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {} + + explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} + + template explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) + : init_(true), storage_(constexpr_forward(args)...) {} + + template >)> + OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list il, Args&&... args) + : init_(true), storage_(il, std::forward(args)...) {} + + ~constexpr_optional_base() = default; + }; + + template + using OptionalBase = typename std::conditional< + is_trivially_destructible::value, + constexpr_optional_base::type>, + optional_base::type> + >::type; + + + + template + class optional : private OptionalBase + { + static_assert( !std::is_same::type, nullopt_t>::value, "bad T" ); + static_assert( !std::is_same::type, in_place_t>::value, "bad T" ); + + + constexpr bool initialized() const noexcept { return OptionalBase::init_; } + typename std::remove_const::type* dataptr() { return std::addressof(OptionalBase::storage_.value_); } + constexpr const T* dataptr() const { return detail_::static_addressof(OptionalBase::storage_.value_); } + +# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 + constexpr const T& contained_val() const& { return OptionalBase::storage_.value_; } +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + OPTIONAL_MUTABLE_CONSTEXPR T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } + OPTIONAL_MUTABLE_CONSTEXPR T& contained_val() & { return OptionalBase::storage_.value_; } +# else + T& contained_val() & { return OptionalBase::storage_.value_; } + T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } +# endif +# else + constexpr const T& contained_val() const { return OptionalBase::storage_.value_; } + T& contained_val() { return OptionalBase::storage_.value_; } +# endif + + void clear() noexcept { + if (initialized()) dataptr()->T::~T(); + OptionalBase::init_ = false; + } + + template + void initialize(Args&&... args) noexcept(noexcept(T(std::forward(args)...))) + { + assert(!OptionalBase::init_); + ::new (static_cast(dataptr())) T(std::forward(args)...); + OptionalBase::init_ = true; + } + + template + void initialize(std::initializer_list il, Args&&... args) noexcept(noexcept(T(il, std::forward(args)...))) + { + assert(!OptionalBase::init_); + ::new (static_cast(dataptr())) T(il, std::forward(args)...); + OptionalBase::init_ = true; + } + + public: + typedef T value_type; + + // 20.5.5.1, constructors + constexpr optional() noexcept : OptionalBase() {}; + constexpr optional(nullopt_t) noexcept : OptionalBase() {}; + + optional(const optional& rhs) + : OptionalBase() + { + if (rhs.initialized()) { + ::new (static_cast(dataptr())) T(*rhs); + OptionalBase::init_ = true; + } + } + + optional(optional&& rhs) noexcept(is_nothrow_move_constructible::value) + : OptionalBase() + { + if (rhs.initialized()) { + ::new (static_cast(dataptr())) T(std::move(*rhs)); + OptionalBase::init_ = true; + } + } + + constexpr optional(const T& v) : OptionalBase(v) {} + + constexpr optional(T&& v) : OptionalBase(constexpr_move(v)) {} + + template + explicit constexpr optional(in_place_t, Args&&... args) + : OptionalBase(in_place_t{}, constexpr_forward(args)...) {} + + template >)> + OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list il, Args&&... args) + : OptionalBase(in_place_t{}, il, constexpr_forward(args)...) {} + + // 20.5.4.2, Destructor + ~optional() = default; + + // 20.5.4.3, assignment + optional& operator=(nullopt_t) noexcept + { + clear(); + return *this; + } + + optional& operator=(const optional& rhs) + { + if (initialized() == true && rhs.initialized() == false) clear(); + else if (initialized() == false && rhs.initialized() == true) initialize(*rhs); + else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs; + return *this; + } + + optional& operator=(optional&& rhs) + noexcept(is_nothrow_move_assignable::value && is_nothrow_move_constructible::value) + { + if (initialized() == true && rhs.initialized() == false) clear(); + else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs)); + else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs); + return *this; + } + + template + auto operator=(U&& v) + -> typename enable_if + < + is_same::type, T>::value, + optional& + >::type + { + if (initialized()) { contained_val() = std::forward(v); } + else { initialize(std::forward(v)); } + return *this; + } + + + template + void emplace(Args&&... args) + { + clear(); + initialize(std::forward(args)...); + } + + template + void emplace(initializer_list il, Args&&... args) + { + clear(); + initialize(il, std::forward(args)...); + } + + // 20.5.4.4, Swap + void swap(optional& rhs) noexcept(is_nothrow_move_constructible::value && noexcept(swap(declval(), declval()))) + { + if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); } + else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); } + else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); } + } + + // 20.5.4.5, Observers + + explicit constexpr operator bool() const noexcept { return initialized(); } + + constexpr T const* operator ->() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); + } + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + + OPTIONAL_MUTABLE_CONSTEXPR T* operator ->() { + assert (initialized()); + return dataptr(); + } + + constexpr T const& operator *() const& { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); + } + + OPTIONAL_MUTABLE_CONSTEXPR T& operator *() & { + assert (initialized()); + return contained_val(); + } + + OPTIONAL_MUTABLE_CONSTEXPR T&& operator *() && { + assert (initialized()); + return constexpr_move(contained_val()); + } + + constexpr T const& value() const& { + return initialized() ? contained_val() : (valijson::throwRuntimeError("bad optional access"), contained_val()); + } + + OPTIONAL_MUTABLE_CONSTEXPR T& value() & { + return initialized() ? contained_val() : (valijson::throwRuntimeError("bad optional access"), contained_val()); + } + + OPTIONAL_MUTABLE_CONSTEXPR T&& value() && { + if (!initialized()) valijson::throwRuntimeError("bad optional access"); + return std::move(contained_val()); + } + +# else + + T* operator ->() { + assert (initialized()); + return dataptr(); + } + + constexpr T const& operator *() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); + } + + T& operator *() { + assert (initialized()); + return contained_val(); + } + + constexpr T const& value() const { + return initialized() ? contained_val() : (valijson::throwRuntimeError("bad optional access"), contained_val()); + } + + T& value() { + return initialized() ? contained_val() : (valijson::throwRuntimeError("bad optional access"), contained_val()); + } + +# endif + +# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 + + template + constexpr T value_or(V&& v) const& + { + return *this ? **this : detail_::convert(constexpr_forward(v)); + } + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + + template + OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) && + { + return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + } + +# else + + template + T value_or(V&& v) && + { + return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + } + +# endif + +# else + + template + constexpr T value_or(V&& v) const + { + return *this ? **this : detail_::convert(constexpr_forward(v)); + } + +# endif + + }; + + + template + class optional + { + static_assert( !std::is_same::value, "bad T" ); + static_assert( !std::is_same::value, "bad T" ); + T* ref; + + public: + + // 20.5.5.1, construction/destruction + constexpr optional() noexcept : ref(nullptr) {} + + constexpr optional(nullopt_t) noexcept : ref(nullptr) {} + + constexpr optional(T& v) noexcept : ref(detail_::static_addressof(v)) {} + + optional(T&&) = delete; + + constexpr optional(const optional& rhs) noexcept : ref(rhs.ref) {} + + explicit constexpr optional(in_place_t, T& v) noexcept : ref(detail_::static_addressof(v)) {} + + explicit optional(in_place_t, T&&) = delete; + + ~optional() = default; + + // 20.5.5.2, mutation + optional& operator=(nullopt_t) noexcept { + ref = nullptr; + return *this; + } + + // optional& operator=(const optional& rhs) noexcept { + // ref = rhs.ref; + // return *this; + // } + + // optional& operator=(optional&& rhs) noexcept { + // ref = rhs.ref; + // return *this; + // } + + template + auto operator=(U&& rhs) noexcept + -> typename enable_if + < + is_same::type, optional>::value, + optional& + >::type + { + ref = rhs.ref; + return *this; + } + + template + auto operator=(U&& rhs) noexcept + -> typename enable_if + < + !is_same::type, optional>::value, + optional& + >::type + = delete; + + void emplace(T& v) noexcept { + ref = detail_::static_addressof(v); + } + + void emplace(T&&) = delete; + + + void swap(optional& rhs) noexcept + { + std::swap(ref, rhs.ref); + } + + // 20.5.5.3, observers + constexpr T* operator->() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); + } + + constexpr T& operator*() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); + } + + constexpr T& value() const { + return ref ? *ref : (valijson::throwRuntimeError("bad optional access"), *ref); + } + + explicit constexpr operator bool() const noexcept { + return ref != nullptr; + } + + template + constexpr typename decay::type value_or(V&& v) const + { + return *this ? **this : detail_::convert::type>(constexpr_forward(v)); + } + }; + + + template + class optional + { + static_assert( sizeof(T) == 0, "optional rvalue references disallowed" ); + }; + + + // 20.5.8, Relational operators + template constexpr bool operator==(const optional& x, const optional& y) + { + return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; + } + + template constexpr bool operator!=(const optional& x, const optional& y) + { + return !(x == y); + } + + template constexpr bool operator<(const optional& x, const optional& y) + { + return (!y) ? false : (!x) ? true : *x < *y; + } + + template constexpr bool operator>(const optional& x, const optional& y) + { + return (y < x); + } + + template constexpr bool operator<=(const optional& x, const optional& y) + { + return !(y < x); + } + + template constexpr bool operator>=(const optional& x, const optional& y) + { + return !(x < y); + } + + + // 20.5.9, Comparison with nullopt + template constexpr bool operator==(const optional& x, nullopt_t) noexcept + { + return (!x); + } + + template constexpr bool operator==(nullopt_t, const optional& x) noexcept + { + return (!x); + } + + template constexpr bool operator!=(const optional& x, nullopt_t) noexcept + { + return bool(x); + } + + template constexpr bool operator!=(nullopt_t, const optional& x) noexcept + { + return bool(x); + } + + template constexpr bool operator<(const optional&, nullopt_t) noexcept + { + return false; + } + + template constexpr bool operator<(nullopt_t, const optional& x) noexcept + { + return bool(x); + } + + template constexpr bool operator<=(const optional& x, nullopt_t) noexcept + { + return (!x); + } + + template constexpr bool operator<=(nullopt_t, const optional&) noexcept + { + return true; + } + + template constexpr bool operator>(const optional& x, nullopt_t) noexcept + { + return bool(x); + } + + template constexpr bool operator>(nullopt_t, const optional&) noexcept + { + return false; + } + + template constexpr bool operator>=(const optional&, nullopt_t) noexcept + { + return true; + } + + template constexpr bool operator>=(nullopt_t, const optional& x) noexcept + { + return (!x); + } + + + + // 20.5.10, Comparison with T + template constexpr bool operator==(const optional& x, const T& v) + { + return bool(x) ? *x == v : false; + } + + template constexpr bool operator==(const T& v, const optional& x) + { + return bool(x) ? v == *x : false; + } + + template constexpr bool operator!=(const optional& x, const T& v) + { + return bool(x) ? *x != v : true; + } + + template constexpr bool operator!=(const T& v, const optional& x) + { + return bool(x) ? v != *x : true; + } + + template constexpr bool operator<(const optional& x, const T& v) + { + return bool(x) ? *x < v : true; + } + + template constexpr bool operator>(const T& v, const optional& x) + { + return bool(x) ? v > *x : true; + } + + template constexpr bool operator>(const optional& x, const T& v) + { + return bool(x) ? *x > v : false; + } + + template constexpr bool operator<(const T& v, const optional& x) + { + return bool(x) ? v < *x : false; + } + + template constexpr bool operator>=(const optional& x, const T& v) + { + return bool(x) ? *x >= v : false; + } + + template constexpr bool operator<=(const T& v, const optional& x) + { + return bool(x) ? v <= *x : false; + } + + template constexpr bool operator<=(const optional& x, const T& v) + { + return bool(x) ? *x <= v : true; + } + + template constexpr bool operator>=(const T& v, const optional& x) + { + return bool(x) ? v >= *x : true; + } + + + // Comparison of optional with T + template constexpr bool operator==(const optional& x, const T& v) + { + return bool(x) ? *x == v : false; + } + + template constexpr bool operator==(const T& v, const optional& x) + { + return bool(x) ? v == *x : false; + } + + template constexpr bool operator!=(const optional& x, const T& v) + { + return bool(x) ? *x != v : true; + } + + template constexpr bool operator!=(const T& v, const optional& x) + { + return bool(x) ? v != *x : true; + } + + template constexpr bool operator<(const optional& x, const T& v) + { + return bool(x) ? *x < v : true; + } + + template constexpr bool operator>(const T& v, const optional& x) + { + return bool(x) ? v > *x : true; + } + + template constexpr bool operator>(const optional& x, const T& v) + { + return bool(x) ? *x > v : false; + } + + template constexpr bool operator<(const T& v, const optional& x) + { + return bool(x) ? v < *x : false; + } + + template constexpr bool operator>=(const optional& x, const T& v) + { + return bool(x) ? *x >= v : false; + } + + template constexpr bool operator<=(const T& v, const optional& x) + { + return bool(x) ? v <= *x : false; + } + + template constexpr bool operator<=(const optional& x, const T& v) + { + return bool(x) ? *x <= v : true; + } + + template constexpr bool operator>=(const T& v, const optional& x) + { + return bool(x) ? v >= *x : true; + } + + // Comparison of optional with T + template constexpr bool operator==(const optional& x, const T& v) + { + return bool(x) ? *x == v : false; + } + + template constexpr bool operator==(const T& v, const optional& x) + { + return bool(x) ? v == *x : false; + } + + template constexpr bool operator!=(const optional& x, const T& v) + { + return bool(x) ? *x != v : true; + } + + template constexpr bool operator!=(const T& v, const optional& x) + { + return bool(x) ? v != *x : true; + } + + template constexpr bool operator<(const optional& x, const T& v) + { + return bool(x) ? *x < v : true; + } + + template constexpr bool operator>(const T& v, const optional& x) + { + return bool(x) ? v > *x : true; + } + + template constexpr bool operator>(const optional& x, const T& v) + { + return bool(x) ? *x > v : false; + } + + template constexpr bool operator<(const T& v, const optional& x) + { + return bool(x) ? v < *x : false; + } + + template constexpr bool operator>=(const optional& x, const T& v) + { + return bool(x) ? *x >= v : false; + } + + template constexpr bool operator<=(const T& v, const optional& x) + { + return bool(x) ? v <= *x : false; + } + + template constexpr bool operator<=(const optional& x, const T& v) + { + return bool(x) ? *x <= v : true; + } + + template constexpr bool operator>=(const T& v, const optional& x) + { + return bool(x) ? v >= *x : true; + } + + + // 20.5.12, Specialized algorithms + template + void swap(optional& x, optional& y) noexcept(noexcept(x.swap(y))) + { + x.swap(y); + } + + + template + constexpr optional::type> make_optional(T&& v) + { + return optional::type>(constexpr_forward(v)); + } + + template + constexpr optional make_optional(reference_wrapper v) + { + return optional(v.get()); + } + + + } // namespace experimental +} // namespace std + +namespace std +{ + template + struct hash> + { + typedef typename hash::result_type result_type; + typedef std::experimental::optional argument_type; + + constexpr result_type operator()(argument_type const& arg) const { + return arg ? std::hash{}(*arg) : result_type{}; + } + }; + + template + struct hash> + { + typedef typename hash::result_type result_type; + typedef std::experimental::optional argument_type; + + constexpr result_type operator()(argument_type const& arg) const { + return arg ? std::hash{}(*arg) : result_type{}; + } + }; +} + +# undef TR2_OPTIONAL_REQUIRES +# undef TR2_OPTIONAL_ASSERTED_EXPRESSION + +# endif //___OPTIONAL_HPP___ diff --git a/subprojects/valijson/include/valijson/adapters/adapter.hpp b/subprojects/valijson/include/valijson/adapters/adapter.hpp new file mode 100644 index 000000000..146156b50 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/adapter.hpp @@ -0,0 +1,472 @@ +#pragma once + +#include + +namespace valijson { +namespace adapters { + +class FrozenValue; + +/** + * @brief An interface that encapsulates access to the JSON values provided + * by a JSON parser implementation. + * + * This interface allows JSON processing code to be parser-agnostic. It provides + * functions to access the plain old datatypes (PODs) that are described in the + * JSON specification, and callback-based access to the contents of arrays and + * objects. + * + * The interface also defines a set of functions that allow for type-casting and + * type-comparison based on value rather than on type. + */ +class Adapter +{ +public: + + /// Typedef for callback function supplied to applyToArray. + typedef std::function + ArrayValueCallback; + + /// Typedef for callback function supplied to applyToObject. + typedef std::function + ObjectMemberCallback; + + /** + * @brief Virtual destructor defined to ensure deletion via base-class + * pointers is safe. + */ + virtual ~Adapter() = default; + + /** + * @brief Apply a callback function to each value in an array. + * + * The callback function is invoked for each element in the array, until + * it has been applied to all values, or it returns false. + * + * @param fn Callback function to invoke + * + * @returns true if Adapter contains an array and all values are equal, + * false otherwise. + */ + virtual bool applyToArray(ArrayValueCallback fn) const = 0; + + /** + * @brief Apply a callback function to each member in an object. + * + * The callback function shall be invoked for each member in the object, + * until it has been applied to all values, or it returns false. + * + * @param fn Callback function to invoke + * + * @returns true if Adapter contains an object, and callback function + * returns true for each member in the object, false otherwise. + */ + virtual bool applyToObject(ObjectMemberCallback fn) const = 0; + + /** + * @brief Return the boolean representation of the contained value. + * + * This function shall return a boolean value if the Adapter contains either + * an actual boolean value, or one of the strings 'true' or 'false'. + * The string comparison is case sensitive. + * + * An exception shall be thrown if the value cannot be cast to a boolean. + * + * @returns Boolean representation of contained value. + */ + virtual bool asBool() const = 0; + + /** + * @brief Retrieve the boolean representation of the contained value. + * + * This function shall retrieve a boolean value if the Adapter contains + * either an actual boolean value, or one of the strings 'true' or 'false'. + * The string comparison is case sensitive. + * + * The retrieved value is returned via reference. + * + * @param result reference to a bool to set with retrieved value. + * + * @returns true if the value could be retrieved, false otherwise + */ + virtual bool asBool(bool &result) const = 0; + + /** + * @brief Return the double representation of the contained value. + * + * This function shall return a double value if the Adapter contains either + * an actual double, an integer, or a string that contains a valid + * representation of a numeric value (according to the C++ Std Library). + * + * An exception shall be thrown if the value cannot be cast to a double. + * + * @returns Double representation of contained value. + */ + virtual double asDouble() const = 0; + + /** + * @brief Retrieve the double representation of the contained value. + * + * This function shall retrieve a double value if the Adapter contains either + * an actual double, an integer, or a string that contains a valid + * representation of a numeric value (according to the C++ Std Library). + * + * The retrieved value is returned via reference. + * + * @param result reference to a double to set with retrieved value. + * + * @returns true if the value could be retrieved, false otherwise + */ + virtual bool asDouble(double &result) const = 0; + + /** + * @brief Return the int64_t representation of the contained value. + * + * This function shall return an int64_t value if the Adapter contains either + * an actual integer, or a string that contains a valid representation of an + * integer value (according to the C++ Std Library). + * + * An exception shall be thrown if the value cannot be cast to an int64_t. + * + * @returns int64_t representation of contained value. + */ + virtual int64_t asInteger() const = 0; + + /** + * @brief Retrieve the int64_t representation of the contained value. + * + * This function shall retrieve an int64_t value if the Adapter contains + * either an actual integer, or a string that contains a valid + * representation of an integer value (according to the C++ Std Library). + * + * The retrieved value is returned via reference. + * + * @param result reference to a int64_t to set with retrieved value. + * + * @returns true if the value could be retrieved, false otherwise + */ + virtual bool asInteger(int64_t &result) const = 0; + + /** + * @brief Return the string representation of the contained value. + * + * This function shall return a string value if the Adapter contains either + * an actual string, a literal value of another POD type, an empty array, + * an empty object, or null. + * + * An exception shall be thrown if the value cannot be cast to a string. + * + * @returns string representation of contained value. + */ + virtual std::string asString() const = 0; + + /** + * @brief Retrieve the string representation of the contained value. + * + * This function shall retrieve a string value if the Adapter contains either + * an actual string, a literal value of another POD type, an empty array, + * an empty object, or null. + * + * The retrieved value is returned via reference. + * + * @param result reference to a string to set with retrieved value. + * + * @returns true if the value could be retrieved, false otherwise + */ + virtual bool asString(std::string &result) const = 0; + + /** + * @brief Compare the value held by this Adapter instance with the value + * held by another Adapter instance. + * + * @param other the other adapter instance + * @param strict flag to use strict type comparison + * + * @returns true if values are equal, false otherwise + */ + virtual bool equalTo(const Adapter &other, bool strict) const = 0; + + /** + * @brief Create a new FrozenValue instance that is equivalent to the + * value contained by the Adapter. + * + * @returns pointer to a new FrozenValue instance, belonging to the caller. + */ + virtual FrozenValue* freeze() const = 0; + + /** + * @brief Return the number of elements in the array. + * + * Throws an exception if the value is not an array. + * + * @return number of elements if value is an array + */ + virtual size_t getArraySize() const = 0; + + /** + * @brief Retrieve the number of elements in the array. + * + * This function shall return true or false to indicate whether or not the + * result value was set. If the contained value is not an array, the + * result value shall not be set. This applies even if the value could be + * cast to an empty array. The calling code is expected to handles those + * cases manually. + * + * @param result reference to size_t variable to set with result. + * + * @return true if value retrieved successfully, false otherwise. + */ + virtual bool getArraySize(size_t &result) const = 0; + + /** + * @brief Return the contained boolean value. + * + * This function shall throw an exception if the contained value is not a + * boolean. + * + * @returns contained boolean value. + */ + virtual bool getBool() const = 0; + + /** + * @brief Retrieve the contained boolean value. + * + * This function shall retrieve the boolean value contained by this Adapter, + * and store it in the result variable that was passed by reference. + * + * @param result reference to boolean variable to set with result. + * + * @returns true if the value was retrieved, false otherwise. + */ + virtual bool getBool(bool &result) const = 0; + + /** + * @brief Return the contained double value. + * + * This function shall throw an exception if the contained value is not a + * double. + * + * @returns contained double value. + */ + virtual double getDouble() const = 0; + + /** + * @brief Retrieve the contained double value. + * + * This function shall retrieve the double value contained by this Adapter, + * and store it in the result variable that was passed by reference. + * + * @param result reference to double variable to set with result. + * + * @returns true if the value was retrieved, false otherwise. + */ + virtual bool getDouble(double &result) const = 0; + + /** + * @brief Return the contained integer value. + * + * This function shall throw an exception if the contained value is not a + * integer. + * + * @returns contained integer value. + */ + virtual int64_t getInteger() const = 0; + + /** + * @brief Retrieve the contained integer value. + * + * This function shall retrieve the integer value contained by this Adapter, + * and store it in the result variable that was passed by reference. + * + * @param result reference to integer variable to set with result. + * + * @returns true if the value was retrieved, false otherwise. + */ + virtual bool getInteger(int64_t &result) const = 0; + + /** + * @brief Return the contained numeric value as a double. + * + * This function shall throw an exception if the contained value is not a + * integer or a double. + * + * @returns contained double or integral value. + */ + virtual double getNumber() const = 0; + + /** + * @brief Retrieve the contained numeric value as a double. + * + * This function shall retrieve the double or integral value contained by + * this Adapter, and store it in the result variable that was passed by + * reference. + * + * @param result reference to double variable to set with result. + * + * @returns true if the value was retrieved, false otherwise. + */ + virtual bool getNumber(double &result) const = 0; + + /** + * @brief Return the number of members in the object. + * + * Throws an exception if the value is not an object. + * + * @return number of members if value is an object + */ + virtual size_t getObjectSize() const = 0; + + /** + * @brief Retrieve the number of members in the object. + * + * This function shall return true or false to indicate whether or not the + * result value was set. If the contained value is not an object, the + * result value shall not be set. This applies even if the value could be + * cast to an empty object. The calling code is expected to handles those + * cases manually. + * + * @param result reference to size_t variable to set with result. + * + * @return true if value retrieved successfully, false otherwise. + */ + virtual bool getObjectSize(size_t &result) const = 0; + + /** + * @brief Return the contained string value. + * + * This function shall throw an exception if the contained value is not a + * string - even if the value could be cast to a string. The asString() + * function should be used when casting is allowed. + * + * @returns string contained by this Adapter + */ + virtual std::string getString() const = 0; + + /** + * @brief Retrieve the contained string value. + * + * This function shall retrieve the string value contained by this Adapter, + * and store it in result variable that is passed by reference. + * + * @param result reference to string to set with result + * + * @returns true if string was retrieved, false otherwise + */ + virtual bool getString(std::string &result) const = 0; + + /** + * @brief Returns whether or not this Adapter supports strict types. + * + * This function shall return true if the Adapter implementation supports + * strict types, or false if the Adapter fails to store any part of the + * type information supported by the Adapter interface. + * + * For example, the PropertyTreeAdapter implementation stores POD values as + * strings, effectively discarding any other type information. If you were + * to call isDouble() on a double stored by this Adapter, the result would + * be false. The maybeDouble(), asDouble() and various related functions + * are provided to perform type checking based on value rather than on type. + * + * The BasicAdapter template class provides implementations for the type- + * casting functions so that Adapter implementations are semantically + * equivalent in their type-casting behaviour. + * + * @returns true if Adapter supports strict types, false otherwise + */ + virtual bool hasStrictTypes() const = 0; + + /// Returns true if the contained value is definitely an array. + virtual bool isArray() const = 0; + + /// Returns true if the contained value is definitely a boolean. + virtual bool isBool() const = 0; + + /// Returns true if the contained value is definitely a double. + virtual bool isDouble() const = 0; + + /// Returns true if the contained value is definitely an integer. + virtual bool isInteger() const = 0; + + /// Returns true if the contained value is definitely a null. + virtual bool isNull() const = 0; + + /// Returns true if the contained value is either a double or an integer. + virtual bool isNumber() const = 0; + + /// Returns true if the contained value is definitely an object. + virtual bool isObject() const = 0; + + /// Returns true if the contained value is definitely a string. + virtual bool isString() const = 0; + + /** + * @brief Returns true if the contained value can be cast to an array. + * + * @returns true if the contained value is an array, an empty string, or an + * empty object. + */ + virtual bool maybeArray() const = 0; + + /** + * @brief Returns true if the contained value can be cast to a boolean. + * + * @returns true if the contained value is a boolean, or one of the strings + * 'true' or 'false'. Note that numeric values are not to be cast + * to boolean values. + */ + virtual bool maybeBool() const = 0; + + /** + * @brief Returns true if the contained value can be cast to a double. + * + * @returns true if the contained value is a double, an integer, or a string + * containing a double or integral value. + */ + virtual bool maybeDouble() const = 0; + + /** + * @brief Returns true if the contained value can be cast to an integer. + * + * @returns true if the contained value is an integer, or a string + * containing an integral value. + */ + virtual bool maybeInteger() const = 0; + + /** + * @brief Returns true if the contained value can be cast to a null. + * + * @returns true if the contained value is null or an empty string. + */ + virtual bool maybeNull() const = 0; + + /** + * @brief Returns true if the contained value can be cast to an object. + * + * @returns true if the contained value is an object, an empty array or + * an empty string. + */ + virtual bool maybeObject() const = 0; + + /** + * @brief Returns true if the contained value can be cast to a string. + * + * @returns true if the contained value is a non-null POD type, an empty + * array, or an empty object. + */ + virtual bool maybeString() const = 0; +}; + +/** + * @brief Template struct that should be specialised for each concrete Adapter + * class. + * + * @deprecated This is a bit of a hack, and I'd like to remove it. + */ +template +struct AdapterTraits +{ + +}; + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/basic_adapter.hpp b/subprojects/valijson/include/valijson/adapters/basic_adapter.hpp new file mode 100644 index 000000000..84b2abdd1 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/basic_adapter.hpp @@ -0,0 +1,859 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace valijson { +namespace adapters { + +/** + * @brief A helper for the array and object member iterators. + * + * See http://www.stlsoft.org/doc-1.9/group__group____pattern____dereference__proxy.html + * for motivation + * + * @tparam Value Name of the value type + */ +template +struct DerefProxy +{ + explicit DerefProxy(const Value& x) + : m_ref(x) { } + + Value* operator->() + { + return std::addressof(m_ref); + } + + explicit operator Value*() + { + return std::addressof(m_ref); + } + +private: + Value m_ref; +}; + +/** + * @brief Template class that implements the expected semantics of an Adapter. + * + * Implementing all of the type-casting functionality for each Adapter is error + * prone and tedious, so this template class aims to minimise the duplication + * of code between various Adapter implementations. This template doesn't quite + * succeed in removing all duplication, but it has greatly simplified the + * implementation of a new Adapter by encapsulating the type-casting semantics + * and a lot of the trivial functionality associated with the Adapter interface. + * + * By inheriting from this template class, Adapter implementations will inherit + * the exception throwing behaviour that is expected by other parts of the + * Valijson library. + * + * @tparam AdapterType Self-referential name of the Adapter being + * specialised. + * @tparam ArrayType Name of the type that will be returned by the + * getArray() function. Instances of this type should + * provide begin(), end() and size() functions so + * that it is possible to iterate over the values in + * the array. + * @tparam ObjectMemberType Name of the type exposed when iterating over the + * contents of an object returned by getObject(). + * @tparam ObjectType Name of the type that will be returned by the + * getObject() function. Instances of this type + * should provide begin(), end(), find() and size() + * functions so that it is possible to iterate over + * the members of the object. + * @tparam ValueType Name of the type that provides a consistent + * interface to a JSON value for a parser. For + * example, this type should provide the getDouble() + * and isDouble() functions. But it does not need to + * know how to cast values from one type to another - + * that functionality is provided by this template + * class. + */ +template< + typename AdapterType, + typename ArrayType, + typename ObjectMemberType, + typename ObjectType, + typename ValueType> +class BasicAdapter: public Adapter +{ +protected: + + /** + * @brief Functor for comparing two arrays. + * + * This functor is used to compare the elements in an array of the type + * ArrayType with individual values provided as generic Adapter objects. + * Comparison is performed by the () operator. + * + * The functor works by maintaining an iterator for the current position + * in an array. Each time the () operator is called, the value at this + * position is compared with the value passed as an argument to (). + * Immediately after the comparison, the iterator will be incremented. + * + * This functor is designed to be passed to the applyToArray() function + * of an Adapter object. + */ + class ArrayComparisonFunctor + { + public: + + /** + * @brief Construct an ArrayComparisonFunctor for an array. + * + * @param array Array to compare values against + * @param strict Flag to use strict type comparison + */ + ArrayComparisonFunctor(const ArrayType &array, bool strict) + : m_itr(array.begin()), + m_end(array.end()), + m_strict(strict) { } + + /** + * @brief Compare a value against the current element in the array. + * + * @param adapter Value to be compared with current element + * + * @returns true if values are equal, false otherwise. + */ + bool operator()(const Adapter &adapter) + { + if (m_itr == m_end) { + return false; + } + + return AdapterType(*m_itr++).equalTo(adapter, m_strict); + } + + private: + + /// Iterator for current element in the array + typename ArrayType::const_iterator m_itr; + + /// Iterator for one-past the last element of the array + typename ArrayType::const_iterator m_end; + + /// Flag to use strict type comparison + const bool m_strict; + }; + + /** + * @brief Functor for comparing two objects + * + * This functor is used to compare the members of an object of the type + * ObjectType with key-value pairs belonging to another object. + * + * The functor works by maintaining a reference to an object provided via + * the constructor. When time the () operator is called with a key-value + * pair as arguments, the function will attempt to find the key in the + * base object. If found, the associated value will be compared with the + * value provided to the () operator. + * + * This functor is designed to be passed to the applyToObject() function + * of an Adapter object. + */ + class ObjectComparisonFunctor + { + public: + + /** + * @brief Construct a new ObjectComparisonFunctor for an object. + * + * @param object object to use as comparison baseline + * @param strict flag to use strict type-checking + */ + ObjectComparisonFunctor(const ObjectType &object, bool strict) + : m_object(object), + m_strict(strict) { } + + /** + * @brief Find a key in the object and compare its value. + * + * @param key Key to find + * @param value Value to be compared against + * + * @returns true if key is found and values are equal, false otherwise. + */ + bool operator()(const std::string &key, const Adapter &value) + { + const typename ObjectType::const_iterator itr = m_object.find(key); + if (itr == m_object.end()) { + return false; + } + + return (*itr).second.equalTo(value, m_strict); + } + + private: + + /// Object to be used as a comparison baseline + const ObjectType &m_object; + + /// Flag to use strict type-checking + bool m_strict; + }; + + +public: + + /// Alias for ArrayType template parameter + typedef ArrayType Array; + + /// Alias for ObjectMemberType template parameter + typedef ObjectMemberType ObjectMember; + + /// Alias for ObjectType template parameter + typedef ObjectType Object; + + /** + * @brief Construct an Adapter using the default value. + * + * This constructor relies on the default constructor of the ValueType + * class provided as a template argument. + */ + BasicAdapter() = default; + + /** + * @brief Construct an Adapter using a specified ValueType object. + * + * This constructor relies on the copy constructor of the ValueType + * class provided as template argument. + */ + explicit BasicAdapter(const ValueType &value) + : m_value(value) { } + + bool applyToArray(ArrayValueCallback fn) const override + { + if (!maybeArray()) { + return false; + } + + // Due to the fact that the only way a value can be 'maybe an array' is + // if it is an empty string or empty object, we only need to go to + // effort of constructing an ArrayType instance if the value is + // definitely an array. + if (m_value.isArray()) { + const opt::optional array = m_value.getArrayOptional(); + for (const AdapterType element : *array) { + if (!fn(element)) { + return false; + } + } + } + + return true; + } + + bool applyToObject(ObjectMemberCallback fn) const override + { + if (!maybeObject()) { + return false; + } + + if (m_value.isObject()) { + const opt::optional object = m_value.getObjectOptional(); + for (const ObjectMemberType member : *object) { + if (!fn(member.first, AdapterType(member.second))) { + return false; + } + } + } + + return true; + } + + /** + * @brief Return an ArrayType instance containing an array representation + * of the value held by this Adapter. + * + * This is a convenience function that is not actually declared in the + * Adapter interface, but allows for useful techniques such as procedural + * iteration over the elements in an array. The ArrayType instance that is + * returned by this function is compatible with the BOOST_FOREACH macro. + * + * If the contained value is either an empty object, or an empty string, + * then this function will cast the value to an empty array. + * + * @returns ArrayType instance containing an array representation of the + * value held by this Adapter. + */ + ArrayType asArray() const + { + if (m_value.isArray()) { + return *m_value.getArrayOptional(); + } else if (m_value.isObject()) { + size_t objectSize; + if (m_value.getObjectSize(objectSize) && objectSize == 0) { + return ArrayType(); + } + } else if (m_value.isString()) { + std::string stringValue; + if (m_value.getString(stringValue) && stringValue.empty()) { + return ArrayType(); + } + } + + throwRuntimeError("JSON value cannot be cast to an array."); + } + + bool asBool() const override + { + bool result; + if (asBool(result)) { + return result; + } + + throwRuntimeError("JSON value cannot be cast to a boolean."); + } + + bool asBool(bool &result) const override + { + if (m_value.isBool()) { + return m_value.getBool(result); + } else if (m_value.isString()) { + std::string s; + if (m_value.getString(s)) { + if (s == "true") { + result = true; + return true; + } else if (s == "false") { + result = false; + return true; + } + } + } + + return false; + } + + double asDouble() const override + { + double result; + if (asDouble(result)) { + return result; + } + + throwRuntimeError("JSON value cannot be cast to a double."); + } + + bool asDouble(double &result) const override + { + if (m_value.isDouble()) { + return m_value.getDouble(result); + } else if (m_value.isInteger()) { + int64_t i; + if (m_value.getInteger(i)) { + result = double(i); + return true; + } + } else if (m_value.isString()) { + std::string s; + if (m_value.getString(s)) { + const char *b = s.c_str(); + char *e = nullptr; + double x = strtod(b, &e); + if (e == b || e != b + s.length()) { + return false; + } + result = x; + return true; + } + } + + return false; + } + + int64_t asInteger() const override + { + int64_t result; + if (asInteger(result)) { + return result; + } + + throwRuntimeError("JSON value cannot be cast as an integer."); + } + + bool asInteger(int64_t &result) const override + { + if (m_value.isInteger()) { + return m_value.getInteger(result); + } else if (m_value.isString()) { + std::string s; + if (m_value.getString(s)) { + std::istringstream i(s); + int64_t x; + char c; + if (!(!(i >> x) || i.get(c))) { + result = x; + return true; + } + } + } + + return false; + } + + /** + * @brief Return an ObjectType instance containing an array representation + * of the value held by this Adapter. + * + * This is a convenience function that is not actually declared in the + * Adapter interface, but allows for useful techniques such as procedural + * iteration over the members of the object. The ObjectType instance that is + * returned by this function is compatible with the BOOST_FOREACH macro. + * + * @returns ObjectType instance containing an object representation of the + * value held by this Adapter. + */ + ObjectType asObject() const + { + if (m_value.isObject()) { + return *m_value.getObjectOptional(); + } else if (m_value.isArray()) { + size_t arraySize; + if (m_value.getArraySize(arraySize) && arraySize == 0) { + return ObjectType(); + } + } else if (m_value.isString()) { + std::string stringValue; + if (m_value.getString(stringValue) && stringValue.empty()) { + return ObjectType(); + } + } + + throwRuntimeError("JSON value cannot be cast to an object."); + } + + std::string asString() const override + { + std::string result; + if (asString(result)) { + return result; + } + + throwRuntimeError("JSON value cannot be cast to a string."); + } + + bool asString(std::string &result) const override + { + if (m_value.isString()) { + return m_value.getString(result); + } else if (m_value.isNull()) { + result.clear(); + return true; + } else if (m_value.isArray()) { + size_t arraySize; + if (m_value.getArraySize(arraySize) && arraySize == 0) { + result.clear(); + return true; + } + } else if (m_value.isObject()) { + size_t objectSize; + if (m_value.getObjectSize(objectSize) && objectSize == 0) { + result.clear(); + return true; + } + } else if (m_value.isBool()) { + bool boolValue; + if (m_value.getBool(boolValue)) { + result = boolValue ? "true" : "false"; + return true; + } + } else if (m_value.isInteger()) { + int64_t integerValue; + if (m_value.getInteger(integerValue)) { + result = std::to_string(integerValue); + return true; + } + } else if (m_value.isDouble()) { + double doubleValue; + if (m_value.getDouble(doubleValue)) { + result = std::to_string(doubleValue); + return true; + } + } + + return false; + } + + bool equalTo(const Adapter &other, bool strict) const override + { + if (isNull() || (!strict && maybeNull())) { + return other.isNull() || (!strict && other.maybeNull()); + } else if (isBool() || (!strict && maybeBool())) { + return (other.isBool() || (!strict && other.maybeBool())) && other.asBool() == asBool(); + } else if (isNumber() && strict) { + return other.isNumber() && other.getNumber() == getNumber(); + } else if (!strict && maybeDouble()) { + return (other.maybeDouble() && other.asDouble() == asDouble()); + } else if (!strict && maybeInteger()) { + return (other.maybeInteger() && other.asInteger() == asInteger()); + } else if (isString() || (!strict && maybeString())) { + return (other.isString() || (!strict && other.maybeString())) && + other.asString() == asString(); + } else if (isArray()) { + if (other.isArray() && getArraySize() == other.getArraySize()) { + const opt::optional array = m_value.getArrayOptional(); + if (array) { + ArrayComparisonFunctor fn(*array, strict); + return other.applyToArray(fn); + } + } else if (!strict && other.maybeArray() && getArraySize() == 0) { + return true; + } + } else if (isObject()) { + if (other.isObject() && other.getObjectSize() == getObjectSize()) { + const opt::optional object = m_value.getObjectOptional(); + if (object) { + ObjectComparisonFunctor fn(*object, strict); + return other.applyToObject(fn); + } + } else if (!strict && other.maybeObject() && getObjectSize() == 0) { + return true; + } + } + + return false; + } + + /** + * @brief Return an ArrayType instance representing the array contained + * by this Adapter instance. + * + * This is a convenience function that is not actually declared in the + * Adapter interface, but allows for useful techniques such as procedural + * iteration over the elements in an array. The ArrayType instance that is + * returned by this function is compatible with the BOOST_FOREACH macro. + * + * If the contained is not an array, this function will throw an exception. + * + * @returns ArrayType instance containing an array representation of the + * value held by this Adapter. + */ + ArrayType getArray() const + { + opt::optional arrayValue = m_value.getArrayOptional(); + if (arrayValue) { + return *arrayValue; + } + + throwRuntimeError("JSON value is not an array."); + } + + size_t getArraySize() const override + { + size_t result; + if (m_value.getArraySize(result)) { + return result; + } + + throwRuntimeError("JSON value is not an array."); + } + + bool getArraySize(size_t &result) const override + { + return m_value.getArraySize(result); + } + + bool getBool() const override + { + bool result; + if (getBool(result)) { + return result; + } + + throwRuntimeError("JSON value is not a boolean."); + } + + bool getBool(bool &result) const override + { + return m_value.getBool(result); + } + + double getDouble() const override + { + double result; + if (getDouble(result)) { + return result; + } + + throwRuntimeError("JSON value is not a double."); + } + + bool getDouble(double &result) const override + { + return m_value.getDouble(result); + } + + int64_t getInteger() const override + { + int64_t result; + if (getInteger(result)) { + return result; + } + + throwRuntimeError("JSON value is not an integer."); + } + + bool getInteger(int64_t &result) const override + { + return m_value.getInteger(result); + } + + double getNumber() const override + { + double result; + if (getNumber(result)) { + return result; + } + + throwRuntimeError("JSON value is not a number."); + } + + bool getNumber(double &result) const override + { + if (isDouble()) { + return getDouble(result); + } else if (isInteger()) { + int64_t integerResult; + if (getInteger(integerResult)) { + result = static_cast(integerResult); + return true; + } + } + + return false; + } + + /** + * @brief Return an ObjectType instance representing the object contained + * by this Adapter instance. + * + * This is a convenience function that is not actually declared in the + * Adapter interface, but allows for useful techniques such as procedural + * iteration over the members of an object. The ObjectType instance that is + * returned by this function is compatible with the BOOST_FOREACH macro. + * + * If the contained is not an object, this function will throw an exception. + * + * @returns ObjectType instance containing an array representation of the + * value held by this Adapter. + */ + ObjectType getObject() const + { + opt::optional objectValue = m_value.getObjectOptional(); + if (objectValue) { + return *objectValue; + } + + throwRuntimeError("JSON value is not an object."); + } + + size_t getObjectSize() const override + { + size_t result; + if (getObjectSize(result)) { + return result; + } + + throwRuntimeError("JSON value is not an object."); + } + + bool getObjectSize(size_t &result) const override + { + return m_value.getObjectSize(result); + } + + std::string getString() const override + { + std::string result; + if (getString(result)) { + return result; + } + + throwRuntimeError("JSON value is not a string."); + } + + bool getString(std::string &result) const override + { + return m_value.getString(result); + } + + FrozenValue * freeze() const override + { + return m_value.freeze(); + } + + bool hasStrictTypes() const override + { + return ValueType::hasStrictTypes(); + } + + bool isArray() const override + { + return m_value.isArray(); + } + + bool isBool() const override + { + return m_value.isBool(); + } + + bool isDouble() const override + { + return m_value.isDouble(); + } + + bool isInteger() const override + { + return m_value.isInteger(); + } + + bool isNull() const override + { + return m_value.isNull(); + } + + bool isNumber() const override + { + return m_value.isInteger() || m_value.isDouble(); + } + + bool isObject() const override + { + return m_value.isObject(); + } + + bool isString() const override + { + return m_value.isString(); + } + + bool maybeArray() const override + { + if (m_value.isArray()) { + return true; + } else if (m_value.isObject()) { + size_t objectSize; + if (m_value.getObjectSize(objectSize) && objectSize == 0) { + return true; + } + } + + return false; + } + + bool maybeBool() const override + { + if (m_value.isBool()) { + return true; + } else if (maybeString()) { + std::string stringValue; + if (m_value.getString(stringValue)) { + if (stringValue == "true" || stringValue == "false") { + return true; + } + } + } + + return false; + } + + bool maybeDouble() const override + { + if (m_value.isNumber()) { + return true; + } else if (maybeString()) { + std::string s; + if (m_value.getString(s)) { + const char *b = s.c_str(); + char *e = nullptr; + strtod(b, &e); + return e != b && e == b + s.length(); + } + } + + return false; + } + + bool maybeInteger() const override + { + if (m_value.isInteger()) { + return true; + } else if (maybeString()) { + std::string s; + if (m_value.getString(s)) { + std::istringstream i(s); + int64_t x; + char c; + if (!(i >> x) || i.get(c)) { + return false; + } + return true; + } + } + + return false; + } + + bool maybeNull() const override + { + if (m_value.isNull()) { + return true; + } else if (maybeString()) { + std::string stringValue; + if (m_value.getString(stringValue)) { + if (stringValue.empty()) { + return true; + } + } + } + + return false; + } + + bool maybeObject() const override + { + if (m_value.isObject()) { + return true; + } else if (maybeArray()) { + size_t arraySize; + if (m_value.getArraySize(arraySize) && arraySize == 0) { + return true; + } + } + + return false; + } + + bool maybeString() const override + { + if (m_value.isString() || m_value.isBool() || m_value.isInteger() || m_value.isDouble()) { + return true; + } else if (m_value.isObject()) { + size_t objectSize; + if (m_value.getObjectSize(objectSize) && objectSize == 0) { + return true; + } + } else if (m_value.isArray()) { + size_t arraySize; + if (m_value.getArraySize(arraySize) && arraySize == 0) { + return true; + } + } + + return false; + } + +private: + + const ValueType m_value; +}; + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/frozen_value.hpp b/subprojects/valijson/include/valijson/adapters/frozen_value.hpp new file mode 100644 index 000000000..56b5eafc5 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/frozen_value.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include + +namespace valijson { +namespace adapters { + +/** + * @brief An interface that provides minimal access to a stored JSON value. + * + * The main reason that this interface exists is to support the 'enum' + * constraint. Each Adapter type is expected to provide an implementation of + * this interface. That class should be able to maintain its own copy of a + * JSON value, independent of the original document. + * + * This interface currently provides just the clone and equalTo functions, but + * could be expanded to include other functions declared in the Adapter + * interface. + * + * @todo it would be nice to better integrate this with the Adapter interface + */ +class FrozenValue +{ +public: + + /** + * @brief Virtual destructor defined to ensure deletion via base-class + * pointers is safe. + */ + virtual ~FrozenValue() { } + + /** + * @brief Clone the stored value and return a pointer to a new FrozenValue + * object containing the value. + */ + virtual FrozenValue *clone() const = 0; + + /** + * @brief Return true if the stored value is equal to the value contained + * by an Adapter instance. + * + * @param adapter Adapter to compare value against + * @param strict Flag to use strict type comparison + * + * @returns true if values are equal, false otherwise + */ + virtual bool equalTo(const Adapter &adapter, bool strict) const = 0; + +}; + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/json11_adapter.hpp b/subprojects/valijson/include/valijson/adapters/json11_adapter.hpp new file mode 100644 index 000000000..41749b2fc --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/json11_adapter.hpp @@ -0,0 +1,711 @@ +/** + * @file + * + * @brief Adapter implementation for the json11 parser library. + * + * Include this file in your program to enable support for json11. + * + * This file defines the following classes (not in this order): + * - Json11Adapter + * - Json11Array + * - Json11ArrayValueIterator + * - Json11FrozenValue + * - Json11Object + * - Json11ObjectMember + * - Json11ObjectMemberIterator + * - Json11Value + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is Json11Adapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +class Json11Adapter; +class Json11ArrayValueIterator; +class Json11ObjectMemberIterator; + +typedef std::pair Json11ObjectMember; + +/** + * @brief Light weight wrapper for a Json11 array value. + * + * This class is light weight wrapper for a Json11 array. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * Json11 value, assumed to be an array, so there is very little overhead + * associated with copy construction and passing by value. + */ +class Json11Array +{ +public: + + typedef Json11ArrayValueIterator const_iterator; + typedef Json11ArrayValueIterator iterator; + + /// Construct a Json11Array referencing an empty array. + Json11Array() + : m_value(emptyArray()) { } + + /** + * @brief Construct a Json11Array referencing a specific Json11 + * value. + * + * @param value reference to a Json11 value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + Json11Array(const json11::Json &value) + : m_value(value) + { + if (!value.is_array()) { + throwRuntimeError("Value is not an array."); + } + } + + /** + * @brief Return an iterator for the first element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying Json11 implementation. + */ + Json11ArrayValueIterator begin() const; + + /** + * @brief Return an iterator for one-past the last element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying Json11 implementation. + */ + Json11ArrayValueIterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + return m_value.array_items().size(); + } + +private: + + /** + * @brief Return a reference to a Json11 value that is an empty array. + * + * Note that the value returned by this function is a singleton. + */ + static const json11::Json & emptyArray() + { + static const json11::Json array((json11::Json::array())); + return array; + } + + /// Reference to the contained value + const json11::Json &m_value; +}; + +/** + * @brief Light weight wrapper for a Json11 object. + * + * This class is light weight wrapper for a Json11 object. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * Json11 value, assumed to be an object, so there is very little overhead + * associated with copy construction and passing by value. + */ +class Json11Object +{ +public: + + typedef Json11ObjectMemberIterator const_iterator; + typedef Json11ObjectMemberIterator iterator; + + /// Construct a Json11Object referencing an empty object singleton. + Json11Object() + : m_value(emptyObject()) { } + + /** + * @brief Construct a Json11Object referencing a specific Json11 + * value. + * + * @param value reference to a Json11 value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + Json11Object(const json11::Json &value) + : m_value(value) + { + if (!value.is_object()) { + throwRuntimeError("Value is not an object."); + } + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying Json11 implementation. + */ + Json11ObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying Json11 implementation. + */ + Json11ObjectMemberIterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param propertyName property name to search for + */ + Json11ObjectMemberIterator find(const std::string &propertyName) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + return m_value.object_items().size(); + } + +private: + + /** + * @brief Return a reference to a Json11 value that is empty object. + * + * Note that the value returned by this function is a singleton. + */ + static const json11::Json & emptyObject() + { + static const json11::Json object((json11::Json::object())); + return object; + } + + /// Reference to the contained object + const json11::Json &m_value; +}; + +/** + * @brief Stores an independent copy of a Json11 value. + * + * This class allows a Json11 value to be stored independent of its original + * document. Json11 makes this easy to do, as it does not perform any + * custom memory management. + * + * @see FrozenValue + */ +class Json11FrozenValue: public FrozenValue +{ +public: + + /** + * @brief Make a copy of a Json11 value + * + * @param source the Json11 value to be copied + */ + explicit Json11FrozenValue(json11::Json source) + : m_value(std::move(source)) { } + + FrozenValue * clone() const override + { + return new Json11FrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /// Stored Json11 value + json11::Json m_value; +}; + +/** + * @brief Light weight wrapper for a Json11 value. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a Json11 value. This class is responsible + * for the mechanics of actually reading a Json11 value, whereas the + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +class Json11Value +{ +public: + + /// Construct a wrapper for the empty object singleton + Json11Value() + : m_value(emptyObject()) { } + + /// Construct a wrapper for a specific Json11 value + Json11Value(const json11::Json &value) + : m_value(value) { } + + /** + * @brief Create a new Json11FrozenValue instance that contains the + * value referenced by this Json11Value instance. + * + * @returns pointer to a new Json11FrozenValue instance, belonging to the + * caller. + */ + FrozenValue * freeze() const + { + return new Json11FrozenValue(m_value); + } + + /** + * @brief Optionally return a Json11Array instance. + * + * If the referenced Json11 value is an array, this function will return + * a std::optional containing a Json11Array instance referencing the + * array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_value.is_array()) { + return opt::make_optional(Json11Array(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced Json11 value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.is_array()) { + result = m_value.array_items().size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.is_bool()) { + result = m_value.bool_value(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.is_number()) { + result = m_value.number_value(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if (isInteger()) { + result = m_value.int_value(); + return true; + } + return false; + } + + /** + * @brief Optionally return a Json11Object instance. + * + * If the referenced Json11 value is an object, this function will return a + * std::optional containing a Json11Object instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_value.is_object()) { + return opt::make_optional(Json11Object(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced Json11 value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.is_object()) { + result = m_value.object_items().size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.is_string()) { + result = m_value.string_value(); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.is_array(); + } + + bool isBool() const + { + return m_value.is_bool(); + } + + bool isDouble() const + { + return m_value.is_number(); + } + + bool isInteger() const + { + return m_value.is_number() && m_value.int_value() == m_value.number_value(); + } + + bool isNull() const + { + return m_value.is_null(); + } + + bool isNumber() const + { + return m_value.is_number(); + } + + bool isObject() const + { + return m_value.is_object(); + } + + bool isString() const + { + return m_value.is_string(); + } + +private: + + /// Return a reference to an empty object singleton + static const json11::Json & emptyObject() + { + static const json11::Json object((json11::Json::object())); + return object; + } + + /// Reference to the contained Json11 value. + const json11::Json &m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting Json11. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +class Json11Adapter: + public BasicAdapter +{ +public: + + /// Construct a Json11Adapter that contains an empty object + Json11Adapter() + : BasicAdapter() { } + + /// Construct a Json11Adapter containing a specific Json11 value + Json11Adapter(const json11::Json &value) + : BasicAdapter(value) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * Json11Adapter representing a value stored in the array. It has been + * implemented using the boost iterator_facade template. + * + * @see Json11Array + */ +class Json11ArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = Json11Adapter; + using difference_type = Json11Adapter; + using pointer = Json11Adapter*; + using reference = Json11Adapter&; + + /** + * @brief Construct a new Json11ArrayValueIterator using an existing + * Json11 iterator. + * + * @param itr Json11 iterator to store + */ + Json11ArrayValueIterator(const json11::Json::array::const_iterator &itr) + : m_itr(itr) { } + + /// Returns a Json11Adapter that contains the value of the current + /// element. + Json11Adapter operator*() const + { + return Json11Adapter(*m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const Json11ArrayValueIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const Json11ArrayValueIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const Json11ArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + Json11ArrayValueIterator operator++(int) + { + Json11ArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const Json11ArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + m_itr += n; + } + +private: + + json11::Json::array::const_iterator m_itr; +}; + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of Json11ObjectMember representing one of the members of the object. It + * has been implemented using the boost iterator_facade template. + * + * @see Json11Object + * @see Json11ObjectMember + */ +class Json11ObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = Json11ObjectMember; + using difference_type = Json11ObjectMember; + using pointer = Json11ObjectMember*; + using reference = Json11ObjectMember&; + + /** + * @brief Construct an iterator from a Json11 iterator. + * + * @param itr Json11 iterator to store + */ + Json11ObjectMemberIterator(const json11::Json::object::const_iterator &itr) + : m_itr(itr) { } + + /** + * @brief Returns a Json11ObjectMember that contains the key and value + * belonging to the object member identified by the iterator. + */ + Json11ObjectMember operator*() const + { + return Json11ObjectMember(m_itr->first, m_itr->second); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const Json11ObjectMemberIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const Json11ObjectMemberIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const Json11ObjectMemberIterator& operator++() + { + m_itr++; + + return *this; + } + + Json11ObjectMemberIterator operator++(int) + { + Json11ObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const Json11ObjectMemberIterator& operator--() + { + m_itr--; + + return *this; + } + +private: + + /// Iternal copy of the original Json11 iterator + json11::Json::object::const_iterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for Json11Adapter. +template<> +struct AdapterTraits +{ + typedef json11::Json DocumentType; + + static std::string adapterName() + { + return "Json11Adapter"; + } +}; + +inline bool Json11FrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return Json11Adapter(m_value).equalTo(other, strict); +} + +inline Json11ArrayValueIterator Json11Array::begin() const +{ + return m_value.array_items().begin(); +} + +inline Json11ArrayValueIterator Json11Array::end() const +{ + return m_value.array_items().end(); +} + +inline Json11ObjectMemberIterator Json11Object::begin() const +{ + return m_value.object_items().begin(); +} + +inline Json11ObjectMemberIterator Json11Object::end() const +{ + return m_value.object_items().end(); +} + +inline Json11ObjectMemberIterator Json11Object::find( + const std::string &propertyName) const +{ + return m_value.object_items().find(propertyName); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/jsoncpp_adapter.hpp b/subprojects/valijson/include/valijson/adapters/jsoncpp_adapter.hpp new file mode 100644 index 000000000..2fd1fe74b --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/jsoncpp_adapter.hpp @@ -0,0 +1,724 @@ +/** + * @file + * + * @brief Adapter implementation for the JsonCpp parser library. + * + * Include this file in your program to enable support for JsonCpp. + * + * This file defines the following classes (not in this order): + * - JsonCppAdapter + * - JsonCppArray + * - JsonCppArrayValueIterator + * - JsonCppFrozenValue + * - JsonCppObject + * - JsonCppObjectMember + * - JsonCppObjectMemberIterator + * - JsonCppValue + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is JsonCppAdapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +class JsonCppAdapter; +class JsonCppArrayValueIterator; +class JsonCppObjectMemberIterator; + +typedef std::pair JsonCppObjectMember; + +/** + * @brief Light weight wrapper for a JsonCpp array value. + * + * This class is light weight wrapper for a JsonCpp array. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * JsonCpp value, assumed to be an array, so there is very little overhead + * associated with copy construction and passing by value. + */ +class JsonCppArray +{ +public: + + typedef JsonCppArrayValueIterator const_iterator; + typedef JsonCppArrayValueIterator iterator; + + /// Construct a JsonCppArray referencing an empty array. + JsonCppArray() + : m_value(emptyArray()) { } + + /** + * @brief Construct a JsonCppArray referencing a specific JsonCpp value. + * + * @param value reference to a JsonCpp value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + JsonCppArray(const Json::Value &value) + : m_value(value) + { + if (!value.isArray()) { + throwRuntimeError("Value is not an array."); + } + } + + /** + * @brief Return an iterator for the first element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying JsonCpp implementation. + */ + JsonCppArrayValueIterator begin() const; + + /** + * @brief Return an iterator for one-past the last element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying JsonCpp implementation. + */ + JsonCppArrayValueIterator end() const; + + /// Return the number of elements in the array. + size_t size() const + { + return m_value.size(); + } + +private: + + /** + * @brief Return a reference to a JsonCpp value that is an empty array. + * + * Note that the value returned by this function is a singleton. + */ + static const Json::Value & emptyArray() + { + static const Json::Value array(Json::arrayValue); + return array; + } + + /// Reference to the contained array + const Json::Value &m_value; + +}; + +/** + * @brief Light weight wrapper for a JsonCpp object. + * + * This class is light weight wrapper for a JsonCpp object. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * JsonCpp object, assumed to be an object, so there is very little overhead + * associated with copy construction and passing by value. + */ +class JsonCppObject +{ +public: + + typedef JsonCppObjectMemberIterator const_iterator; + typedef JsonCppObjectMemberIterator iterator; + + /// Construct a JsonCppObject referencing an empty object singleton. + JsonCppObject() + : m_value(emptyObject()) { } + + /** + * @brief Construct a JsonCppObject referencing a specific JsonCpp value. + * + * @param value reference to a JsonCpp value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + JsonCppObject(const Json::Value &value) + : m_value(value) + { + if (!value.isObject()) { + throwRuntimeError("Value is not an object."); + } + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying JsonCpp implementation. + */ + JsonCppObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying JsonCpp implementation. + */ + JsonCppObjectMemberIterator end() const; + + /** + * @brief Return an iterator for a member/property with the given name + * + * @param propertyName Property name + * + * @returns a valid iterator if found, or an invalid iterator if not found + */ + JsonCppObjectMemberIterator find(const std::string &propertyName) const; + + /// Return the number of members in the object + size_t size() const + { + return m_value.size(); + } + +private: + + /// Return a reference to an empty JsonCpp object + static const Json::Value & emptyObject() + { + static const Json::Value object(Json::objectValue); + return object; + } + + /// Reference to the contained object + const Json::Value &m_value; +}; + +/** + * @brief Stores an independent copy of a JsonCpp value. + * + * This class allows a JsonCpp value to be stored independent of its original + * document. JsonCpp makes this easy to do, as it does not perform any + * custom memory management. + * + * @see FrozenValue + */ +class JsonCppFrozenValue: public FrozenValue +{ +public: + + /** + * @brief Make a copy of a JsonCpp value + * + * @param source the JsonCpp value to be copied + */ + explicit JsonCppFrozenValue(const Json::Value &source) + : m_value(source) { } + + FrozenValue * clone() const override + { + return new JsonCppFrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /// Stored JsonCpp value + Json::Value m_value; +}; + +/** + * @brief Light weight wrapper for a JsonCpp value. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a JsonCpp value. This class is responsible + * for the mechanics of actually reading a JsonCpp value, whereas the + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +class JsonCppValue +{ +public: + + /// Construct a wrapper for the empty object singleton + JsonCppValue() + : m_value(emptyObject()) { } + + /// Construct a wrapper for a specific JsonCpp value + JsonCppValue(const Json::Value &value) + : m_value(value) { } + + /** + * @brief Create a new JsonCppFrozenValue instance that contains the + * value referenced by this JsonCppValue instance. + * + * @returns pointer to a new JsonCppFrozenValue instance, belonging to the + * caller. + */ + FrozenValue * freeze() const + { + return new JsonCppFrozenValue(m_value); + } + + /** + * @brief Optionally return a JsonCppArray instance. + * + * If the referenced JsonCpp value is an array, this function will return a + * std::optional containing a JsonCppArray instance referencing the + * array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_value.isArray()) { + return opt::make_optional(JsonCppArray(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced JsonCpp value is an array, this function will retrieve + * the number of elements in the array and store it in the output variable + * provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.isArray()) { + result = m_value.size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.isBool()) { + result = m_value.asBool(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.isDouble()) { + result = m_value.asDouble(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if (m_value.isIntegral()) { + result = static_cast(m_value.asInt()); + return true; + } + + return false; + } + + /** + * @brief Optionally return a JsonCppObject instance. + * + * If the referenced JsonCpp value is an object, this function will return a + * std::optional containing a JsonCppObject instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_value.isObject()) { + return opt::make_optional(JsonCppObject(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced JsonCpp value is an object, this function will retrieve + * the number of members in the object and store it in the output variable + * provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.isObject()) { + result = m_value.size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.isString()) { + result = m_value.asString(); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.isArray() && !m_value.isNull(); + } + + bool isBool() const + { + return m_value.isBool(); + } + + bool isDouble() const + { + return m_value.isDouble(); + } + + bool isInteger() const + { + return m_value.isIntegral() && !m_value.isBool(); + } + + bool isNull() const + { + return m_value.isNull(); + } + + bool isNumber() const + { + return m_value.isNumeric() && !m_value.isBool(); + } + + bool isObject() const + { + return m_value.isObject() && !m_value.isNull(); + } + + bool isString() const + { + return m_value.isString(); + } + +private: + + /// Return a reference to an empty object singleton. + static const Json::Value &emptyObject() + { + static Json::Value object(Json::objectValue); + return object; + } + + /// Reference to the contained JsonCpp value + const Json::Value &m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting JsonCpp. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +class JsonCppAdapter: + public BasicAdapter +{ +public: + + /// Construct a JsonCppAdapter that contains an empty object + JsonCppAdapter() + : BasicAdapter() { } + + /// Construct a JsonCppAdapter containing a specific JsonCpp value + JsonCppAdapter(const Json::Value &value) + : BasicAdapter(value) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * JsonCppAdapter representing a value stored in the array. It has been + * implemented using the boost iterator_facade template. + * + * @see JsonCppArray + */ +class JsonCppArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = JsonCppAdapter; + using difference_type = JsonCppAdapter; + using pointer = JsonCppAdapter*; + using reference = JsonCppAdapter&; + + /** + * @brief Construct a new JsonCppArrayValueIterator using an existing + * JsonCpp iterator. + * + * @param itr JsonCpp iterator to store + */ + JsonCppArrayValueIterator(const Json::Value::const_iterator &itr) + : m_itr(itr) { } + + /// Returns a JsonCppAdapter that contains the value of the current element. + JsonCppAdapter operator*() const + { + return JsonCppAdapter(*m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param rhs iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const JsonCppArrayValueIterator &rhs) const + { + return m_itr == rhs.m_itr; + } + + bool operator!=(const JsonCppArrayValueIterator &rhs) const + { + return !(m_itr == rhs.m_itr); + } + + JsonCppArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + JsonCppArrayValueIterator operator++(int) + { + JsonCppArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + JsonCppArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + if (n > 0) { + while (n-- > 0) { + m_itr++; + } + } else { + while (n++ < 0) { + m_itr--; + } + } + } + +private: + + Json::Value::const_iterator m_itr; +}; + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of JsonCppObjectMember representing one of the members of the object. It has + * been implemented using the boost iterator_facade template. + * + * @see JsonCppObject + * @see JsonCppObjectMember + */ +class JsonCppObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = JsonCppObjectMember; + using difference_type = JsonCppObjectMember; + using pointer = JsonCppObjectMember*; + using reference = JsonCppObjectMember&; + + /** + * @brief Construct an iterator from a JsonCpp iterator. + * + * @param itr JsonCpp iterator to store + */ + JsonCppObjectMemberIterator(const Json::ValueConstIterator &itr) + : m_itr(itr) { } + + /** + * @brief Returns a JsonCppObjectMember that contains the key and value + * belonging to the object member identified by the iterator. + */ + JsonCppObjectMember operator*() const + { + return JsonCppObjectMember(m_itr.key().asString(), *m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param rhs Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const JsonCppObjectMemberIterator &rhs) const + { + return m_itr == rhs.m_itr; + } + + bool operator!=(const JsonCppObjectMemberIterator &rhs) const + { + return !(m_itr == rhs.m_itr); + } + + const JsonCppObjectMemberIterator& operator++() + { + m_itr++; + + return *this; + } + + JsonCppObjectMemberIterator operator++(int) + { + JsonCppObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + JsonCppObjectMemberIterator operator--() + { + m_itr--; + + return *this; + } + +private: + + /// Iternal copy of the original JsonCpp iterator + Json::ValueConstIterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for JsonCppAdapter. +template<> +struct AdapterTraits +{ + typedef Json::Value DocumentType; + + static std::string adapterName() + { + return "JsonCppAdapter"; + } +}; + +inline bool JsonCppFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return JsonCppAdapter(m_value).equalTo(other, strict); +} + +inline JsonCppArrayValueIterator JsonCppArray::begin() const +{ + return m_value.begin(); +} + +inline JsonCppArrayValueIterator JsonCppArray::end() const +{ + return m_value.end(); +} + +inline JsonCppObjectMemberIterator JsonCppObject::begin() const +{ + return m_value.begin(); +} + +inline JsonCppObjectMemberIterator JsonCppObject::end() const +{ + return m_value.end(); +} + +inline JsonCppObjectMemberIterator JsonCppObject::find( + const std::string &propertyName) const +{ + if (m_value.isMember(propertyName)) { + Json::ValueConstIterator itr; + for (itr = m_value.begin(); itr != m_value.end(); ++itr) { + if (itr.key() == propertyName) { + return itr; + } + } + } + + return m_value.end(); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/nlohmann_json_adapter.hpp b/subprojects/valijson/include/valijson/adapters/nlohmann_json_adapter.hpp new file mode 100644 index 000000000..aeb156476 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/nlohmann_json_adapter.hpp @@ -0,0 +1,713 @@ +/** + * @file + * + * @brief Adapter implementation for the nlohmann json parser library. + * + * Include this file in your program to enable support for nlohmann json. + * + * This file defines the following classes (not in this order): + * - NlohmannJsonAdapter + * - NlohmannJsonArray + * - NlohmannJsonValueIterator + * - NlohmannJsonFrozenValue + * - NlohmannJsonObject + * - NlohmannJsonObjectMember + * - NlohmannJsonObjectMemberIterator + * - NlohmannJsonValue + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is NlohmannJsonAdapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +class NlohmannJsonAdapter; +class NlohmannJsonArrayValueIterator; +class NlohmannJsonObjectMemberIterator; + +typedef std::pair NlohmannJsonObjectMember; + +/** + * @brief Light weight wrapper for a NlohmannJson array value. + * + * This class is light weight wrapper for a NlohmannJson array. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * NlohmannJson value, assumed to be an array, so there is very little overhead + * associated with copy construction and passing by value. + */ +class NlohmannJsonArray +{ +public: + + typedef NlohmannJsonArrayValueIterator const_iterator; + typedef NlohmannJsonArrayValueIterator iterator; + + /// Construct a NlohmannJsonArray referencing an empty array. + NlohmannJsonArray() + : m_value(emptyArray()) { } + + /** + * @brief Construct a NlohmannJsonArray referencing a specific NlohmannJson + * value. + * + * @param value reference to a NlohmannJson value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + NlohmannJsonArray(const nlohmann::json &value) + : m_value(value) + { + if (!value.is_array()) { + throwRuntimeError("Value is not an array."); + } + } + + /** + * @brief Return an iterator for the first element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying NlohmannJson implementation. + */ + NlohmannJsonArrayValueIterator begin() const; + + /** + * @brief Return an iterator for one-past the last element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying NlohmannJson implementation. + */ + NlohmannJsonArrayValueIterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + return m_value.size(); + } + +private: + + /** + * @brief Return a reference to a NlohmannJson value that is an empty array. + * + * Note that the value returned by this function is a singleton. + */ + static const nlohmann::json & emptyArray() + { + static const nlohmann::json array = nlohmann::json::array(); + return array; + } + + /// Reference to the contained value + const nlohmann::json &m_value; +}; + +/** + * @brief Light weight wrapper for a NlohmannJson object. + * + * This class is light weight wrapper for a NlohmannJson object. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * NlohmannJson value, assumed to be an object, so there is very little overhead + * associated with copy construction and passing by value. + */ +class NlohmannJsonObject +{ +public: + + typedef NlohmannJsonObjectMemberIterator const_iterator; + typedef NlohmannJsonObjectMemberIterator iterator; + + /// Construct a NlohmannJsonObject referencing an empty object singleton. + NlohmannJsonObject() + : m_value(emptyObject()) { } + + /** + * @brief Construct a NlohmannJsonObject referencing a specific NlohmannJson + * value. + * + * @param value reference to a NlohmannJson value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + NlohmannJsonObject(const nlohmann::json &value) + : m_value(value) + { + if (!value.is_object()) { + throwRuntimeError("Value is not an object."); + } + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying NlohmannJson implementation. + */ + NlohmannJsonObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying NlohmannJson implementation. + */ + NlohmannJsonObjectMemberIterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param propertyName property name to search for + */ + NlohmannJsonObjectMemberIterator find(const std::string &propertyName) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + return m_value.size(); + } + +private: + + /** + * @brief Return a reference to a NlohmannJson value that is empty object. + * + * Note that the value returned by this function is a singleton. + */ + static const nlohmann::json & emptyObject() + { + static const nlohmann::json object = nlohmann::json::object(); + return object; + } + + /// Reference to the contained object + const nlohmann::json &m_value; +}; + + +/** + * @brief Stores an independent copy of a NlohmannJson value. + * + * This class allows a NlohmannJson value to be stored independent of its original + * document. NlohmannJson makes this easy to do, as it does not perform any + * custom memory management. + * + * @see FrozenValue + */ +class NlohmannJsonFrozenValue: public FrozenValue +{ +public: + + /** + * @brief Make a copy of a NlohmannJson value + * + * @param source the NlohmannJson value to be copied + */ + explicit NlohmannJsonFrozenValue(nlohmann::json source) + : m_value(std::move(source)) { } + + FrozenValue * clone() const override + { + return new NlohmannJsonFrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /// Stored NlohmannJson value + nlohmann::json m_value; +}; + + +/** + * @brief Light weight wrapper for a NlohmannJson value. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a NlohmannJson value. This class is responsible + * for the mechanics of actually reading a NlohmannJson value, whereas the + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +class NlohmannJsonValue +{ +public: + + /// Construct a wrapper for the empty object singleton + NlohmannJsonValue() + : m_value(emptyObject()) { } + + /// Construct a wrapper for a specific NlohmannJson value + NlohmannJsonValue(const nlohmann::json &value) + : m_value(value) { } + + /** + * @brief Create a new NlohmannJsonFrozenValue instance that contains the + * value referenced by this NlohmannJsonValue instance. + * + * @returns pointer to a new NlohmannJsonFrozenValue instance, belonging to the + * caller. + */ + FrozenValue * freeze() const + { + return new NlohmannJsonFrozenValue(m_value); + } + + /** + * @brief Optionally return a NlohmannJsonArray instance. + * + * If the referenced NlohmannJson value is an array, this function will return + * a std::optional containing a NlohmannJsonArray instance referencing the + * array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_value.is_array()) { + return opt::make_optional(NlohmannJsonArray(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced NlohmannJson value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.is_array()) { + result = m_value.size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.is_boolean()) { + result = m_value.get(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.is_number_float()) { + result = m_value.get(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if(m_value.is_number_integer()) { + result = m_value.get(); + return true; + } + return false; + } + + /** + * @brief Optionally return a NlohmannJsonObject instance. + * + * If the referenced NlohmannJson value is an object, this function will return a + * std::optional containing a NlohmannJsonObject instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_value.is_object()) { + return opt::make_optional(NlohmannJsonObject(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced NlohmannJson value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.is_object()) { + result = m_value.size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.is_string()) { + result = m_value.get(); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.is_array(); + } + + bool isBool() const + { + return m_value.is_boolean(); + } + + bool isDouble() const + { + return m_value.is_number_float(); + } + + bool isInteger() const + { + return m_value.is_number_integer(); + } + + bool isNull() const + { + return m_value.is_null(); + } + + bool isNumber() const + { + return m_value.is_number(); + } + + bool isObject() const + { + return m_value.is_object(); + } + + bool isString() const + { + return m_value.is_string(); + } + +private: + + /// Return a reference to an empty object singleton + static const nlohmann::json & emptyObject() + { + static const nlohmann::json object = nlohmann::json::object(); + return object; + } + + /// Reference to the contained NlohmannJson value. + const nlohmann::json &m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting NlohmannJson. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +class NlohmannJsonAdapter: + public BasicAdapter +{ +public: + /// Construct a NlohmannJsonAdapter that contains an empty object + NlohmannJsonAdapter() + : BasicAdapter() { } + + /// Construct a NlohmannJsonAdapter containing a specific Nlohmann Json object + NlohmannJsonAdapter(const nlohmann::json &value) + : BasicAdapter(NlohmannJsonValue{value}) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * NlohmannJsonAdapter representing a value stored in the array. It has been + * implemented using the boost iterator_facade template. + * + * @see NlohmannJsonArray + */ +class NlohmannJsonArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = NlohmannJsonAdapter; + using difference_type = NlohmannJsonAdapter; + using pointer = NlohmannJsonAdapter*; + using reference = NlohmannJsonAdapter&; + + /** + * @brief Construct a new NlohmannJsonArrayValueIterator using an existing + * NlohmannJson iterator. + * + * @param itr NlohmannJson iterator to store + */ + NlohmannJsonArrayValueIterator(const nlohmann::json::const_iterator &itr) + : m_itr(itr) { } + + /// Returns a NlohmannJsonAdapter that contains the value of the current + /// element. + NlohmannJsonAdapter operator*() const + { + return NlohmannJsonAdapter(*m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const NlohmannJsonArrayValueIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const NlohmannJsonArrayValueIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const NlohmannJsonArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + NlohmannJsonArrayValueIterator operator++(int) + { + NlohmannJsonArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const NlohmannJsonArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + m_itr += n; + } + +private: + nlohmann::json::const_iterator m_itr; +}; + + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of NlohmannJsonObjectMember representing one of the members of the object. It + * has been implemented using the boost iterator_facade template. + * + * @see NlohmannJsonObject + * @see NlohmannJsonObjectMember + */ +class NlohmannJsonObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = NlohmannJsonObjectMember; + using difference_type = NlohmannJsonObjectMember; + using pointer = NlohmannJsonObjectMember*; + using reference = NlohmannJsonObjectMember&; + + /** + * @brief Construct an iterator from a NlohmannJson iterator. + * + * @param itr NlohmannJson iterator to store + */ + NlohmannJsonObjectMemberIterator(const nlohmann::json::const_iterator &itr) + : m_itr(itr) { } + + /** + * @brief Returns a NlohmannJsonObjectMember that contains the key and value + * belonging to the object member identified by the iterator. + */ + NlohmannJsonObjectMember operator*() const + { + return NlohmannJsonObjectMember(m_itr.key(), m_itr.value()); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const NlohmannJsonObjectMemberIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const NlohmannJsonObjectMemberIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const NlohmannJsonObjectMemberIterator& operator++() + { + m_itr++; + + return *this; + } + + NlohmannJsonObjectMemberIterator operator++(int) + { + NlohmannJsonObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const NlohmannJsonObjectMemberIterator& operator--() + { + m_itr--; + + return *this; + } + +private: + + /// Iternal copy of the original NlohmannJson iterator + nlohmann::json::const_iterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for NlohmannJsonAdapter. +template<> +struct AdapterTraits +{ + typedef nlohmann::json DocumentType; + + static std::string adapterName() + { + return "NlohmannJsonAdapter"; + } +}; + +inline bool NlohmannJsonFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return NlohmannJsonAdapter(m_value).equalTo(other, strict); +} + +inline NlohmannJsonArrayValueIterator NlohmannJsonArray::begin() const +{ + return m_value.begin(); +} + +inline NlohmannJsonArrayValueIterator NlohmannJsonArray::end() const +{ + return m_value.end(); +} + +inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::begin() const +{ + return m_value.begin(); +} + +inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::end() const +{ + return m_value.end(); +} + +inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::find( + const std::string &propertyName) const +{ + return m_value.find(propertyName); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/picojson_adapter.hpp b/subprojects/valijson/include/valijson/adapters/picojson_adapter.hpp new file mode 100644 index 000000000..4f5e5b6b7 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/picojson_adapter.hpp @@ -0,0 +1,732 @@ +/** + * @file + * + * @brief Adapter implementation for the PicoJson parser library. + * + * Include this file in your program to enable support for PicoJson. + * + * This file defines the following classes (not in this order): + * - PicoJsonAdapter + * - PicoJsonArray + * - PicoJsonArrayValueIterator + * - PicoJsonFrozenValue + * - PicoJsonObject + * - PicoJsonObjectMember + * - PicoJsonObjectMemberIterator + * - PicoJsonValue + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is PicoJsonAdapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include + +#ifdef _MSC_VER +#pragma warning(disable: 4706) +#include +#pragma warning(default: 4706) +#else +#include +#endif + +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +class PicoJsonAdapter; +class PicoJsonArrayValueIterator; +class PicoJsonObjectMemberIterator; + +typedef std::pair PicoJsonObjectMember; + +/** + * @brief Light weight wrapper for a PicoJson array value. + * + * This class is light weight wrapper for a PicoJson array. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * PicoJson value, assumed to be an array, so there is very little overhead + * associated with copy construction and passing by value. + */ +class PicoJsonArray +{ +public: + + typedef PicoJsonArrayValueIterator const_iterator; + typedef PicoJsonArrayValueIterator iterator; + + /// Construct a PicoJsonArray referencing an empty array. + PicoJsonArray() + : m_value(emptyArray()) { } + + /** + * @brief Construct a PicoJsonArray referencing a specific PicoJson + * value. + * + * @param value reference to a PicoJson value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + explicit PicoJsonArray(const picojson::value &value) + : m_value(value) + { + if (!value.is()) { + throwRuntimeError("Value is not an array."); + } + } + + /** + * @brief Return an iterator for the first element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying PicoJson implementation. + */ + PicoJsonArrayValueIterator begin() const; + + /** + * @brief Return an iterator for one-past the last element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying PicoJson implementation. + */ + PicoJsonArrayValueIterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + const picojson::array &array = m_value.get(); + return array.size(); + } + +private: + + /** + * @brief Return a reference to a PicoJson value that is an empty array. + * + * Note that the value returned by this function is a singleton. + */ + static const picojson::value & emptyArray() + { + static const picojson::value array(picojson::array_type, false); + return array; + } + + /// Reference to the contained value + const picojson::value &m_value; +}; + +/** + * @brief Light weight wrapper for a PicoJson object. + * + * This class is light weight wrapper for a PicoJson object. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * PicoJson value, assumed to be an object, so there is very little overhead + * associated with copy construction and passing by value. + */ +class PicoJsonObject +{ +public: + + typedef PicoJsonObjectMemberIterator const_iterator; + typedef PicoJsonObjectMemberIterator iterator; + + /// Construct a PicoJsonObject referencing an empty object singleton. + PicoJsonObject() + : m_value(emptyObject()) { } + + /** + * @brief Construct a PicoJsonObject referencing a specific PicoJson + * value. + * + * @param value reference to a PicoJson value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + PicoJsonObject(const picojson::value &value) + : m_value(value) + { + if (!value.is()) { + throwRuntimeError("Value is not an object."); + } + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying PicoJson implementation. + */ + PicoJsonObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying PicoJson implementation. + */ + PicoJsonObjectMemberIterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param propertyName property name to search for + */ + PicoJsonObjectMemberIterator find(const std::string &propertyName) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + const picojson::object &object = m_value.get(); + return object.size(); + } + +private: + + /** + * @brief Return a reference to a PicoJson value that is empty object. + * + * Note that the value returned by this function is a singleton. + */ + static const picojson::value & emptyObject() + { + static const picojson::value object(picojson::object_type, false); + return object; + } + + /// Reference to the contained object + const picojson::value &m_value; +}; + +/** + * @brief Stores an independent copy of a PicoJson value. + * + * This class allows a PicoJson value to be stored independent of its original + * document. PicoJson makes this easy to do, as it does not perform any + * custom memory management. + * + * @see FrozenValue + */ +class PicoJsonFrozenValue: public FrozenValue +{ +public: + + /** + * @brief Make a copy of a PicoJson value + * + * @param source the PicoJson value to be copied + */ + explicit PicoJsonFrozenValue(const picojson::value &source) + : m_value(source) { } + + FrozenValue * clone() const override + { + return new PicoJsonFrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /// Stored PicoJson value + picojson::value m_value; +}; + +/** + * @brief Light weight wrapper for a PicoJson value. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a PicoJson value. This class is responsible + * for the mechanics of actually reading a PicoJson value, whereas the + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +class PicoJsonValue +{ +public: + + /// Construct a wrapper for the empty object singleton + PicoJsonValue() + : m_value(emptyObject()) { } + + /// Construct a wrapper for a specific PicoJson value + PicoJsonValue(const picojson::value &value) + : m_value(value) { } + + /** + * @brief Create a new PicoJsonFrozenValue instance that contains the + * value referenced by this PicoJsonValue instance. + * + * @returns pointer to a new PicoJsonFrozenValue instance, belonging to the + * caller. + */ + FrozenValue * freeze() const + { + return new PicoJsonFrozenValue(m_value); + } + + /** + * @brief Optionally return a PicoJsonArray instance. + * + * If the referenced PicoJson value is an array, this function will return + * a std::optional containing a PicoJsonArray instance referencing the + * array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_value.is()) { + return opt::make_optional(PicoJsonArray(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced PicoJson value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.is()) { + const picojson::array& array = m_value.get(); + result = array.size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.is()) { + result = m_value.get(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.is()) { + result = m_value.get(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if (m_value.is()) { + result = m_value.get(); + return true; + } + + return false; + } + + /** + * @brief Optionally return a PicoJsonObject instance. + * + * If the referenced PicoJson value is an object, this function will return a + * std::optional containing a PicoJsonObject instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_value.is()) { + return opt::make_optional(PicoJsonObject(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced PicoJson value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.is()) { + const picojson::object &object = m_value.get(); + result = object.size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.is()) { + result = m_value.get(); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.is(); + } + + bool isBool() const + { + return m_value.is(); + } + + bool isDouble() const + { + if (m_value.is()) { + return false; + } + + return m_value.is(); + } + + bool isInteger() const + { + return m_value.is(); + } + + bool isNull() const + { + return m_value.is(); + } + + bool isNumber() const + { + return m_value.is(); + } + + bool isObject() const + { + return m_value.is(); + } + + bool isString() const + { + return m_value.is(); + } + +private: + + /// Return a reference to an empty object singleton + static const picojson::value & emptyObject() + { + static const picojson::value object(picojson::object_type, false); + return object; + } + + /// Reference to the contained PicoJson value. + const picojson::value &m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting PicoJson. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +class PicoJsonAdapter: + public BasicAdapter +{ +public: + + /// Construct a PicoJsonAdapter that contains an empty object + PicoJsonAdapter() + : BasicAdapter() { } + + /// Construct a PicoJsonAdapter containing a specific PicoJson value + PicoJsonAdapter(const picojson::value &value) + : BasicAdapter(value) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * PicoJsonAdapter representing a value stored in the array. It has been + * implemented using the std::iterator template. + * + * @see PicoJsonArray + */ +class PicoJsonArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = PicoJsonAdapter; + using difference_type = PicoJsonAdapter; + using pointer = PicoJsonAdapter*; + using reference = PicoJsonAdapter&; + + /** + * @brief Construct a new PicoJsonArrayValueIterator using an existing + * PicoJson iterator. + * + * @param itr PicoJson iterator to store + */ + PicoJsonArrayValueIterator(const picojson::array::const_iterator &itr) + : m_itr(itr) { } + + /// Returns a PicoJsonAdapter that contains the value of the current + /// element. + PicoJsonAdapter operator*() const + { + return PicoJsonAdapter(*m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const PicoJsonArrayValueIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const PicoJsonArrayValueIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const PicoJsonArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + PicoJsonArrayValueIterator operator++(int) + { + PicoJsonArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const PicoJsonArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + m_itr += n; + } + +private: + + picojson::array::const_iterator m_itr; +}; + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of PicoJsonObjectMember representing one of the members of the object. It + * has been implemented using the boost iterator_facade template. + * + * @see PicoJsonObject + * @see PicoJsonObjectMember + */ +class PicoJsonObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = PicoJsonObjectMember; + using difference_type = PicoJsonObjectMember; + using pointer = PicoJsonObjectMember*; + using reference = PicoJsonObjectMember&; + + /** + * @brief Construct an iterator from a PicoJson iterator. + * + * @param itr PicoJson iterator to store + */ + PicoJsonObjectMemberIterator(const picojson::object::const_iterator &itr) + : m_itr(itr) { } + + /** + * @brief Returns a PicoJsonObjectMember that contains the key and value + * belonging to the object member identified by the iterator. + */ + PicoJsonObjectMember operator*() const + { + return PicoJsonObjectMember(m_itr->first, m_itr->second); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const PicoJsonObjectMemberIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const PicoJsonObjectMemberIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const PicoJsonObjectMemberIterator& operator++() + { + m_itr++; + + return *this; + } + + PicoJsonObjectMemberIterator operator++(int) + { + PicoJsonObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const PicoJsonObjectMemberIterator& operator--(int) + { + m_itr--; + + return *this; + } + +private: + + /// Iternal copy of the original PicoJson iterator + picojson::object::const_iterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for PicoJsonAdapter. +template<> +struct AdapterTraits +{ + typedef picojson::value DocumentType; + + static std::string adapterName() + { + return "PicoJsonAdapter"; + } +}; + +inline bool PicoJsonFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return PicoJsonAdapter(m_value).equalTo(other, strict); +} + +inline PicoJsonArrayValueIterator PicoJsonArray::begin() const +{ + const picojson::array &array = m_value.get(); + return array.begin(); +} + +inline PicoJsonArrayValueIterator PicoJsonArray::end() const +{ + const picojson::array &array = m_value.get(); + return array.end(); +} + +inline PicoJsonObjectMemberIterator PicoJsonObject::begin() const +{ + const picojson::object &object = m_value.get(); + return object.begin(); +} + +inline PicoJsonObjectMemberIterator PicoJsonObject::end() const +{ + const picojson::object &object = m_value.get(); + return object.end(); +} + +inline PicoJsonObjectMemberIterator PicoJsonObject::find( + const std::string &propertyName) const +{ + const picojson::object &object = m_value.get(); + return object.find(propertyName); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/poco_json_adapter.hpp b/subprojects/valijson/include/valijson/adapters/poco_json_adapter.hpp new file mode 100644 index 000000000..a997debfa --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/poco_json_adapter.hpp @@ -0,0 +1,720 @@ +/** +* @file +* +* @brief Adapter implementation for the Poco json parser library. +* +* Include this file in your program to enable support for Poco json. +* +* This file defines the following classes (not in this order): +* - PocoJsonAdapter +* - PocoJsonArray +* - PocoJsonValueIterator +* - PocoJsonFrozenValue +* - PocoJsonObject +* - PocoJsonObjectMember +* - PocoJsonObjectMemberIterator +* - PocoJsonValue +* +* Due to the dependencies that exist between these classes, the ordering of +* class declarations and definitions may be a bit confusing. The best place to +* start is PocoJsonAdapter. This class definition is actually very small, +* since most of the functionality is inherited from the BasicAdapter class. +* Most of the classes in this file are provided as template arguments to the +* inherited BasicAdapter class. +*/ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +class PocoJsonAdapter; +class PocoJsonArrayValueIterator; +class PocoJsonObjectMemberIterator; + +typedef std::pair PocoJsonObjectMember; + +/** +* @brief Light weight wrapper for a PocoJson array value. +* +* This class is light weight wrapper for a PocoJson array. It provides a +* minimum set of container functions and typedefs that allow it to be used as +* an iterable container. +* +* An instance of this class contains a single reference to the underlying +* PocoJson value, assumed to be an array, so there is very little overhead +* associated with copy construction and passing by value. +*/ +class PocoJsonArray +{ +public: + + typedef PocoJsonArrayValueIterator const_iterator; + typedef PocoJsonArrayValueIterator iterator; + + /// Construct a PocoJsonArray referencing an empty array. + PocoJsonArray() + : m_value(emptyArray()) + { } + + /** + * @brief Construct a PocoJsonArray referencing a specific PocoJson + * value. + * + * @param value reference to a PocoJson value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + PocoJsonArray(const Poco::Dynamic::Var &value) + : m_value(value) + { + if (value.type() != typeid(Poco::JSON::Array::Ptr)) { + throwRuntimeError("Value is not an array."); + } + } + + /** + * @brief Return an iterator for the first element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying PocoJson implementation. + */ + PocoJsonArrayValueIterator begin() const; + + /** + * @brief Return an iterator for one-past the last element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying PocoJson implementation. + */ + PocoJsonArrayValueIterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + return m_value.extract()->size(); + } + +private: + + /** + * @brief Return a PocoJson value that is an empty array. + */ + static Poco::Dynamic::Var emptyArray() + { + Poco::Dynamic::Var array = Poco::JSON::Array::Ptr(); + return array; + } + + /// Contained value + Poco::Dynamic::Var m_value; +}; + +/** +* @brief Light weight wrapper for a PocoJson object. +* +* This class is light weight wrapper for a PocoJson object. It provides a +* minimum set of container functions and typedefs that allow it to be used as +* an iterable container. +* +* An instance of this class contains a single reference to the underlying +* PocoJson value, assumed to be an object, so there is very little overhead +* associated with copy construction and passing by value. +*/ +class PocoJsonObject +{ +public: + + typedef PocoJsonObjectMemberIterator const_iterator; + typedef PocoJsonObjectMemberIterator iterator; + + /// Construct a PocoJsonObject an empty object. + PocoJsonObject() + : m_value(emptyObject()) + { } + + /** + * @brief Construct a PocoJsonObject referencing a specific PocoJson + * value. + * + * @param value reference to a PocoJson value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + PocoJsonObject(const Poco::Dynamic::Var &value) + : m_value(value) + { + if (value.type() != typeid(Poco::JSON::Object::Ptr)) { + throwRuntimeError("Value is not an object."); + } + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying PocoJson implementation. + */ + PocoJsonObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying PocoJson implementation. + */ + PocoJsonObjectMemberIterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param propertyName property name to search for + */ + PocoJsonObjectMemberIterator find(const std::string &propertyName) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + return m_value.extract()->size(); + } + +private: + + /** + * @brief Return a PocoJson value that is empty object. + */ + static Poco::Dynamic::Var emptyObject() + { + Poco::Dynamic::Var object = Poco::JSON::Object::Ptr(); + return object; + } + + /// Contained value + Poco::Dynamic::Var m_value; +}; + +/** +* @brief Stores an independent copy of a PocoJson value. +* +* This class allows a PocoJson value to be stored independent of its original +* document. PocoJson makes this easy to do, as it does not perform any +* custom memory management. +* +* @see FrozenValue +*/ +class PocoJsonFrozenValue : public FrozenValue +{ +public: + + /** + * @brief Make a copy of a PocoJson value + * + * @param source the PocoJson value to be copied + */ + explicit PocoJsonFrozenValue(const Poco::Dynamic::Var &source) + : m_value(source) + { } + + virtual FrozenValue * clone() const + { + return new PocoJsonFrozenValue(m_value); + } + + virtual bool equalTo(const Adapter &other, bool strict) const; + +private: + + /// Stored PocoJson value + Poco::Dynamic::Var m_value; +}; + + +/** +* @brief Light weight wrapper for a PocoJson value. +* +* This class is passed as an argument to the BasicAdapter template class, +* and is used to provide access to a PocoJson value. This class is responsible +* for the mechanics of actually reading a PocoJson value, whereas the +* BasicAdapter class is responsible for the semantics of type comparisons +* and conversions. +* +* The functions that need to be provided by this class are defined implicitly +* by the implementation of the BasicAdapter template class. +* +* @see BasicAdapter +*/ +class PocoJsonValue +{ +public: + + /// Construct a wrapper for the empty object + PocoJsonValue() + : m_value(emptyObject()) + { } + + /// Construct a wrapper for a specific PocoJson value + PocoJsonValue(const Poco::Dynamic::Var& value) + : m_value(value) + { } + + /** + * @brief Create a new PocoJsonFrozenValue instance that contains the + * value referenced by this PocoJsonValue instance. + * + * @returns pointer to a new PocoJsonFrozenValue instance, belonging to the + * caller. + */ + FrozenValue * freeze() const + { + return new PocoJsonFrozenValue(m_value); + } + + /** + * @brief Optionally return a PocoJsonArray instance. + * + * If the referenced PocoJson value is an array, this function will return + * a std::optional containing a PocoJsonArray instance referencing the + * array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_value.type() == typeid(Poco::JSON::Array::Ptr)) { + return opt::make_optional(PocoJsonArray(m_value)); + } + + return opt::optional(); + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced PocoJson value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.type() == typeid(Poco::JSON::Array::Ptr)) { + result = m_value.extract()->size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.isBoolean()) { + result = m_value.convert(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.isNumeric() && !m_value.isInteger()) { + result = m_value.convert(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if (m_value.isInteger()) { + result = m_value.convert(); + return true; + } + return false; + } + + /** + * @brief Optionally return a PocoJsonObject instance. + * + * If the referenced PocoJson value is an object, this function will return a + * std::optional containing a PocoJsonObject instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_value.type() == typeid(Poco::JSON::Object::Ptr)) { + return opt::make_optional(PocoJsonObject(m_value)); + } + + return opt::optional(); + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced PocoJson value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.type() == typeid(Poco::JSON::Object::Ptr)) { + result = m_value.extract()->size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.isString()) { + result = m_value.convert(); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.type() == typeid(Poco::JSON::Array::Ptr); + } + + bool isBool() const + { + return m_value.isBoolean(); + } + + bool isDouble() const + { + return m_value.isNumeric() && !m_value.isInteger(); + } + + bool isInteger() const + { + return !isBool() && m_value.isInteger(); + } + + bool isNull() const + { + return m_value.isEmpty(); + } + + bool isNumber() const + { + return m_value.isNumeric(); + } + + bool isObject() const + { + return m_value.type() == typeid(Poco::JSON::Object::Ptr); + } + + bool isString() const + { + return m_value.isString(); + } + +private: + + /// Return an empty object + static Poco::Dynamic::Var emptyObject() + { + Poco::Dynamic::Var object = Poco::JSON::Object::Ptr(); + return object; + } + + /// Contained value + Poco::Dynamic::Var m_value; +}; + +/** +* @brief An implementation of the Adapter interface supporting PocoJson. +* +* This class is defined in terms of the BasicAdapter template class, which +* helps to ensure that all of the Adapter implementations behave consistently. +* +* @see Adapter +* @see BasicAdapter +*/ +class PocoJsonAdapter : + public BasicAdapter +{ +public: + /// Construct a PocoJsonAdapter that contains an empty object + PocoJsonAdapter() + : BasicAdapter() + { } + + /// Construct a PocoJsonAdapter containing a specific Poco Json object + PocoJsonAdapter(const Poco::Dynamic::Var &value) + : BasicAdapter(PocoJsonValue {value}) + { } +}; + +/** +* @brief Class for iterating over values held in a JSON array. +* +* This class provides a JSON array iterator that dereferences as an instance of +* PocoJsonAdapter representing a value stored in the array. It has been +* implemented using the boost iterator_facade template. +* +* @see PocoJsonArray +*/ +class PocoJsonArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = PocoJsonAdapter; + using difference_type = PocoJsonAdapter; + using pointer = PocoJsonAdapter*; + using reference = PocoJsonAdapter&; + + /** + * @brief Construct a new PocoJsonArrayValueIterator using an existing + * PocoJson iterator. + * + * @param itr PocoJson iterator to store + */ + PocoJsonArrayValueIterator(const Poco::JSON::Array::ConstIterator &itr) + : m_itr(itr) + { } + + /// Returns a PocoJsonAdapter that contains the value of the current + /// element. + PocoJsonAdapter operator*() const + { + return PocoJsonAdapter(*m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const PocoJsonArrayValueIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const PocoJsonArrayValueIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const PocoJsonArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + PocoJsonArrayValueIterator operator++(int) + { + PocoJsonArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const PocoJsonArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + m_itr += n; + } + +private: + Poco::JSON::Array::ConstIterator m_itr; +}; + + +/** +* @brief Class for iterating over the members belonging to a JSON object. +* +* This class provides a JSON object iterator that dereferences as an instance +* of PocoJsonObjectMember representing one of the members of the object. It +* has been implemented using the boost iterator_facade template. +* +* @see PocoJsonObject +* @see PocoJsonObjectMember +*/ +class PocoJsonObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = PocoJsonObjectMember; + using difference_type = PocoJsonObjectMember; + using pointer = PocoJsonObjectMember*; + using reference = PocoJsonObjectMember&; + + /** + * @brief Construct an iterator from a PocoJson iterator. + * + * @param itr PocoJson iterator to store + */ + PocoJsonObjectMemberIterator(const Poco::JSON::Object::ConstIterator &itr) + : m_itr(itr) + { } + + /** + * @brief Returns a PocoJsonObjectMember that contains the key and value + * belonging to the object member identified by the iterator. + */ + PocoJsonObjectMember operator*() const + { + return PocoJsonObjectMember(m_itr->first, m_itr->second); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const PocoJsonObjectMemberIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const PocoJsonObjectMemberIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const PocoJsonObjectMemberIterator& operator++() + { + m_itr++; + + return *this; + } + + PocoJsonObjectMemberIterator operator++(int) + { + PocoJsonObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const PocoJsonObjectMemberIterator& operator--() + { + m_itr--; + + return *this; + } + +private: + + /// Iternal copy of the original PocoJson iterator + Poco::JSON::Object::ConstIterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for PocoJsonAdapter. +template<> +struct AdapterTraits +{ + typedef Poco::Dynamic::Var DocumentType; + + static std::string adapterName() + { + return "PocoJsonAdapter"; + } +}; + +inline PocoJsonArrayValueIterator PocoJsonArray::begin() const +{ + return m_value.extract()->begin(); +} + +inline PocoJsonArrayValueIterator PocoJsonArray::end() const +{ + return m_value.extract()->end(); +} + +inline PocoJsonObjectMemberIterator PocoJsonObject::begin() const +{ + return m_value.extract()->begin(); +} + +inline PocoJsonObjectMemberIterator PocoJsonObject::end() const +{ + return m_value.extract()->end(); +} + +inline PocoJsonObjectMemberIterator PocoJsonObject::find(const std::string &propertyName) const +{ + auto& ptr = m_value.extract(); + + auto it = std::find_if(ptr->begin(), ptr->end(), [&propertyName](const Poco::JSON::Object::ValueType& p) { + return p.first == propertyName; + }); + return it; +} + +inline bool PocoJsonFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return PocoJsonAdapter(m_value).equalTo(other, strict); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/property_tree_adapter.hpp b/subprojects/valijson/include/valijson/adapters/property_tree_adapter.hpp new file mode 100644 index 000000000..425547fb6 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/property_tree_adapter.hpp @@ -0,0 +1,753 @@ +/** + * @file + * + * @brief Adapter implementation for the Boost property tree library. + * + * Include this file in your program to enable support for boost property trees. + * + * This file defines the following classes (not in this order): + * - PropertyTreeAdapter + * - PropertyTreeArray + * - PropertyTreeArrayValueIterator + * - PropertyTreeFrozenValue + * - PropertyTreeObject + * - PropertyTreeObjectMember + * - PropertyTreeObjectMemberIterator + * - PropertyTreeValue + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is PropertyTreeAdapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace valijson { +namespace adapters { + +class PropertyTreeAdapter; +class PropertyTreeArrayValueIterator; +class PropertyTreeObjectMemberIterator; + +typedef std::pair PropertyTreeObjectMember; + +/** + * @brief Light weight wrapper for a Boost property tree that contains + * array-like data. + * + * This class is light weight wrapper for a Boost property tree. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to a Boost property + * tree that is assumed to contain unnamed key-value pairs. There is very little + * associated with copy construction and passing by value. + */ +class PropertyTreeArray +{ +public: + + typedef PropertyTreeArrayValueIterator const_iterator; + typedef PropertyTreeArrayValueIterator iterator; + + /// Construct a PropertyTreeArray referencing an empty property tree + /// singleton. + PropertyTreeArray() + : m_array(emptyTree()) { } + + /** + * @brief Construct PropertyTreeArray referencing a specific Boost + * property tree. + * + * @param array reference to a property tree containing an array + * + * It is assumed that this value contains array-like data, but this is not + * checked due to runtime cost. + */ + explicit PropertyTreeArray(const boost::property_tree::ptree &array) + : m_array(array) { } + + /// Return an iterator for the first element in the array. + PropertyTreeArrayValueIterator begin() const; + + /// Return an iterator for one-past the last element of the array. + PropertyTreeArrayValueIterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + return m_array.size(); + } + +private: + + /** + * @brief Return a reference to a property tree that looks like an + * empty array. + * + * Note that the value returned by this function is a singleton. + */ + static const boost::property_tree::ptree & emptyTree() + { + static const boost::property_tree::ptree tree; + return tree; + } + + /// Reference to the contained value + const boost::property_tree::ptree &m_array; +}; + +/** + * @brief Light weight wrapper for a Boost property tree that contains + * object-like data. + * + * This class is light weight wrapper for a Boost property tree. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * property tree value, assumed to be object-like, so there is very little + * overhead associated with copy construction and passing by value. + */ +class PropertyTreeObject +{ +public: + + typedef PropertyTreeObjectMemberIterator const_iterator; + typedef PropertyTreeObjectMemberIterator iterator; + + /// Construct a PropertyTreeObject referencing an empty property tree. + PropertyTreeObject() + : m_object(emptyTree()) { } + + /** + * @brief Construct a PropertyTreeObject referencing a specific property + * tree. + * + * @param object reference to a property tree containing an object + * + * Note that the value of the property tree is not checked, due to the + * runtime cost of doing so. + */ + PropertyTreeObject(const boost::property_tree::ptree &object) + : m_object(object) { } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying property tree + * implementation. + */ + PropertyTreeObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the pointer value returned by the underlying property tree + * implementation. + */ + PropertyTreeObjectMemberIterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param property property name to search for + */ + PropertyTreeObjectMemberIterator find(const std::string &property) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + return m_object.size(); + } + +private: + + /** + * @brief Return a reference to an empty property tree. + * + * Note that the value returned by this function is a singleton. + */ + static const boost::property_tree::ptree & emptyTree() + { + static const boost::property_tree::ptree tree; + return tree; + } + + /// Reference to the contained object + const boost::property_tree::ptree &m_object; + +}; + +/** + * @brief Stores an independent copy of a Boost property tree. + * + * This class allows a property tree value to be stored independent of its + * original 'document'. Boost property trees make this easy to do, as they do + * not perform any custom memory management. + * + * @see FrozenValue + */ +class PropertyTreeFrozenValue: public FrozenValue +{ +public: + + /** + * @brief Make a copy of a Boost property tree POD value + * + * @param source string containing the POD vlaue + */ + explicit PropertyTreeFrozenValue(const boost::property_tree::ptree::data_type &source) + : m_value(source) { } + + /** + * @brief Make a copy of a Boost property tree object or array value + * + * @param source the property tree to be copied + */ + explicit PropertyTreeFrozenValue(const boost::property_tree::ptree &source) + : m_value(source) { } + + FrozenValue * clone() const override + { + return new PropertyTreeFrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /// Stored value + boost::property_tree::ptree m_value; +}; + +/** + * @brief Light weight wrapper for a Boost property tree. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a Boost property tree value. This class + * is responsible for the mechanics of actually reading a property tree, whereas + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +class PropertyTreeValue +{ +public: + + /// Construct a wrapper for an empty property tree + PropertyTreeValue() + : m_object(emptyTree()) { } + + /** + * @brief Construct a PropertyTreeValue from a tree object + * + * This function will determine whether the tree object represents an array + * or an object by scanning the key names for any non-empty strings. In the + * case of an empty tree object, it is not possible to determine whether it + * is an array or an object, so it will be treated as an array by default. + * Empty arrays are considered equal to empty objects when compared using + * non-strict type comparison. Empty strings will also be stored as empty + * arrays. + * + * @param tree Tree object to be wrapped + */ + PropertyTreeValue(const boost::property_tree::ptree &tree) + { + if (tree.data().empty()) { // No string content + if (tree.empty()) { // No children + m_array.emplace(tree); // Treat as empty array + } else { + bool isArray = true; + for (const auto &node : tree) { + if (!node.first.empty()) { + isArray = false; + break; + } + } + + if (isArray) { + m_array.emplace(tree); + } else { + m_object.emplace(tree); + } + } + } else { + m_value = tree.data(); + } + } + + /** + * @brief Create a new PropertyTreeFrozenValue instance that contains the + * value referenced by this PropertyTreeValue instance. + * + * @returns pointer to a new PropertyTreeFrozenValue instance, belonging to + * the caller. + */ + FrozenValue* freeze() const + { + if (m_array) { + return new PropertyTreeFrozenValue(*m_array); + } else if (m_object) { + return new PropertyTreeFrozenValue(*m_object); + } else { + return new PropertyTreeFrozenValue(*m_value); + } + } + + /** + * @brief Return an instance of PropertyTreeArrayAdapter. + * + * If the referenced property tree value is an array, this function will + * return a std::optional containing a PropertyTreeArray instance + * referencing the array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_array) { + return opt::make_optional(PropertyTreeArray(*m_array)); + } + + return {}; + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced property tree value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_array) { + result = m_array->size(); + return true; + } + + return false; + } + + static bool getBool(bool &) + { + return false; + } + + static bool getDouble(double &) + { + return false; + } + + static bool getInteger(int64_t &) + { + return false; + } + + /** + * @brief Optionally return a PropertyTreeObject instance. + * + * If the referenced property tree is an object, this function will return a + * std::optional containing a PropertyTreeObject instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_object) { + return opt::make_optional(PropertyTreeObject(*m_object)); + } + + return {}; + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced property tree value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_object) { + result = m_object->size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value) { + result = *m_value; + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return false; + } + + bool isArray() const + { + return static_cast(m_array); + } + + static bool isBool() + { + return false; + } + + static bool isDouble() + { + return false; + } + + static bool isInteger() + { + return false; + } + + static bool isNull() + { + return false; + } + + static bool isNumber() + { + return false; + } + + bool isObject() const + { + return static_cast(m_object); + } + + bool isString() const + { + return static_cast(m_value); + } + +private: + + static const boost::property_tree::ptree & emptyTree() + { + static const boost::property_tree::ptree tree; + return tree; + } + + /// Reference used if the value is known to be an array + opt::optional m_array; + + /// Reference used if the value is known to be an object + opt::optional m_object; + + /// Reference used if the value is known to be a POD type + opt::optional m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting the Boost + * property tree library. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +class PropertyTreeAdapter: + public BasicAdapter +{ +public: + + /// Construct a PropertyTreeAdapter for an empty property tree + PropertyTreeAdapter() + : BasicAdapter() { } + + /// Construct a PropertyTreeAdapter using a specific property tree + PropertyTreeAdapter(const boost::property_tree::ptree &value) + : BasicAdapter(value) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * PropertyTreeAdapter representing a value stored in the array. It has been + * implemented using the boost iterator_facade template. + * + * @see PropertyTreeArray + */ +class PropertyTreeArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = PropertyTreeAdapter; + using difference_type = PropertyTreeAdapter; + using pointer = PropertyTreeAdapter*; + using reference = PropertyTreeAdapter&; + + /** + * @brief Construct a new PropertyTreeArrayValueIterator using an existing + * property tree iterator. + * + * @param itr property tree iterator to store + */ + PropertyTreeArrayValueIterator( + const boost::property_tree::ptree::const_iterator &itr) + : m_itr(itr) { } + + /// Returns a PropertyTreeAdapter that contains the value of the current + /// element. + PropertyTreeAdapter operator*() const + { + return PropertyTreeAdapter(m_itr->second); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param rhs iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const PropertyTreeArrayValueIterator &rhs) const + { + return m_itr == rhs.m_itr; + } + + bool operator!=(const PropertyTreeArrayValueIterator &rhs) const + { + return !(m_itr == rhs.m_itr); + } + + const PropertyTreeArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + PropertyTreeArrayValueIterator operator++(int) + { + PropertyTreeArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const PropertyTreeArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + if (n > 0) { + while (n-- > 0) { + m_itr++; + } + } else { + while (n++ < 0) { + m_itr--; + } + } + } + +private: + + boost::property_tree::ptree::const_iterator m_itr; +}; + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of PropertyTreeObjectMember representing one of the members of the object. + * It has been implemented using the boost iterator_facade template. + * + * @see PropertyTreeObject + * @see PropertyTreeObjectMember + */ +class PropertyTreeObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = PropertyTreeObjectMember; + using difference_type = PropertyTreeObjectMember; + using pointer = PropertyTreeObjectMember*; + using reference = PropertyTreeObjectMember&; + + /** + * @brief Construct an iterator from a PropertyTree iterator. + * + * @param itr PropertyTree iterator to store + */ + PropertyTreeObjectMemberIterator( + boost::property_tree::ptree::const_assoc_iterator itr) + : m_itr(itr) { } + + /** + * @brief Returns a PropertyTreeObjectMember that contains the key and + * value belonging to the object member identified by the iterator. + */ + PropertyTreeObjectMember operator*() const + { + return PropertyTreeObjectMember(m_itr->first, m_itr->second); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param rhs Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const PropertyTreeObjectMemberIterator &rhs) const + { + return m_itr == rhs.m_itr; + } + + bool operator!=(const PropertyTreeObjectMemberIterator &rhs) const + { + return !(m_itr == rhs.m_itr); + } + + const PropertyTreeObjectMemberIterator& operator++() + { + m_itr++; + + return *this; + } + + PropertyTreeObjectMemberIterator operator++(int) + { + PropertyTreeObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const PropertyTreeObjectMemberIterator& operator--() + { + m_itr--; + + return *this; + } + +private: + + boost::property_tree::ptree::const_assoc_iterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for PropertyTreeAdapter. +template<> +struct AdapterTraits +{ + typedef boost::property_tree::ptree DocumentType; + + static std::string adapterName() + { + return "PropertyTreeAdapter"; + } +}; + +inline bool PropertyTreeFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return PropertyTreeAdapter(m_value).equalTo(other, strict); +} + +inline PropertyTreeArrayValueIterator PropertyTreeArray::begin() const +{ + return m_array.begin(); +} + +inline PropertyTreeArrayValueIterator PropertyTreeArray::end() const +{ + return m_array.end(); +} + +inline PropertyTreeObjectMemberIterator PropertyTreeObject::begin() const +{ + return m_object.ordered_begin(); +} + +inline PropertyTreeObjectMemberIterator PropertyTreeObject::end() const +{ + return m_object.not_found(); +} + +inline PropertyTreeObjectMemberIterator PropertyTreeObject::find( + const std::string &propertyName) const +{ + const boost::property_tree::ptree::const_assoc_iterator itr = m_object.find(propertyName); + + if (itr != m_object.not_found()) { + return itr; + } + + return m_object.not_found(); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/qtjson_adapter.hpp b/subprojects/valijson/include/valijson/adapters/qtjson_adapter.hpp new file mode 100644 index 000000000..2307fb5c3 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/qtjson_adapter.hpp @@ -0,0 +1,722 @@ +/** + * @file + * + * @brief Adapter implementation for the QtJson parser library. + * + * Include this file in your program to enable support for QtJson. + * + * This file defines the following classes (not in this order): + * - QtJsonAdapter + * - QtJsonArray + * - QtJsonArrayValueIterator + * - QtJsonFrozenValue + * - QtJsonObject + * - QtJsonObjectMember + * - QtJsonObjectMemberIterator + * - QtJsonValue + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is QtJsonAdapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +class QtJsonAdapter; +class QtJsonArrayValueIterator; +class QtJsonObjectMemberIterator; + +typedef std::pair QtJsonObjectMember; + +/** + * @brief Light weight wrapper for a QtJson array value. + * + * This class is light weight wrapper for a QtJson array. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * QtJson value, assumed to be an array, so there is very little overhead + * associated with copy construction and passing by value. + */ +class QtJsonArray +{ +public: + + typedef QtJsonArrayValueIterator const_iterator; + typedef QtJsonArrayValueIterator iterator; + + /// Construct a QtJsonArray referencing an empty array. + QtJsonArray() + : m_value(emptyArray()) + { + } + + /** + * @brief Construct a QtJsonArray referencing a specific QtJson + * value. + * + * @param value reference to a QtJson value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + explicit QtJsonArray(const QJsonValue &value) + : m_value(value.toArray()) + { + if (!value.isArray()) { + throwRuntimeError("Value is not an array."); + } + } + + /** + * @brief Return an iterator for the first element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying QtJson implementation. + */ + QtJsonArrayValueIterator begin() const; + + /** + * @brief Return an iterator for one-past the last element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying QtJson implementation. + */ + QtJsonArrayValueIterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + return m_value.size(); + } + +private: + + /** + * @brief Return a reference to a QtJson value that is an empty array. + * + * Note that the value returned by this function is a singleton. + */ + static QJsonArray emptyArray() + { + static const QJsonArray array; + return array; + } + + /// Reference to the contained value + const QJsonArray m_value; +}; + +/** + * @brief Light weight wrapper for a QtJson object. + * + * This class is light weight wrapper for a QtJson object. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * QtJson value, assumed to be an object, so there is very little overhead + * associated with copy construction and passing by value. + */ +class QtJsonObject +{ +public: + + typedef QtJsonObjectMemberIterator const_iterator; + typedef QtJsonObjectMemberIterator iterator; + + /// Construct a QtJsonObject referencing an empty object singleton. + QtJsonObject() + : m_value(emptyObject()) + { + } + + /** + * @brief Construct a QtJsonObject referencing a specific QtJson + * value. + * + * @param value reference to a QtJson value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + QtJsonObject(const QJsonValue &value) + : m_value(value.toObject()) + { + if (!value.isObject()) { + throwRuntimeError("Value is not an object."); + } + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying QtJson implementation. + */ + QtJsonObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying QtJson implementation. + */ + QtJsonObjectMemberIterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param propertyName property name to search for + */ + QtJsonObjectMemberIterator find(const std::string &propertyName) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + return m_value.size(); + } + +private: + + /** + * @brief Return a reference to a QtJson value that is empty object. + * + * Note that the value returned by this function is a singleton. + */ + static const QJsonObject emptyObject() + { + static const QJsonObject object; + return object; + } + + /// Reference to the contained object + const QJsonObject m_value; +}; + +/** + * @brief Stores an independent copy of a QtJson value. + * + * This class allows a QtJson value to be stored independent of its original + * document. QtJson makes this easy to do, as it does not perform any + * custom memory management. + * + * @see FrozenValue + */ +class QtJsonFrozenValue: public FrozenValue +{ +public: + + /** + * @brief Make a copy of a QtJson value + * + * @param source the QtJson value to be copied + */ + explicit QtJsonFrozenValue(QJsonValue source) + : m_value(std::move(source)) { } + + FrozenValue * clone() const override + { + return new QtJsonFrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /// Stored QtJson value + QJsonValue m_value; +}; + +/** + * @brief Light weight wrapper for a QtJson value. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a QtJson value. This class is responsible + * for the mechanics of actually reading a QtJson value, whereas the + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +class QtJsonValue +{ +public: + + /// Construct a wrapper for the empty object singleton + QtJsonValue() + : m_value(emptyObject()) { } + + /// Construct a wrapper for a specific QtJson value + QtJsonValue(QJsonValue value) + : m_value(std::move(value)) { } + + /** + * @brief Create a new QtJsonFrozenValue instance that contains the + * value referenced by this QtJsonValue instance. + * + * @returns pointer to a new QtJsonFrozenValue instance, belonging to the + * caller. + */ + FrozenValue * freeze() const + { + return new QtJsonFrozenValue(m_value); + } + + /** + * @brief Optionally return a QtJsonArray instance. + * + * If the referenced QtJson value is an array, this function will return + * a std::optional containing a QtJsonArray instance referencing the + * array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_value.isArray()) { + return opt::make_optional(QtJsonArray(m_value)); + } + + return opt::optional(); + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced QtJson value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.isArray()) { + const QJsonArray array = m_value.toArray(); + result = array.size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.isBool()) { + result = m_value.toBool(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.isDouble()) { + result = m_value.toDouble(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if (m_value.isDouble()) { + result = m_value.toInt(); + return true; + } + + return false; + } + + /** + * @brief Optionally return a QtJsonObject instance. + * + * If the referenced QtJson value is an object, this function will return a + * std::optional containing a QtJsonObject instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_value.isObject()) { + return opt::make_optional(QtJsonObject(m_value)); + } + + return opt::optional(); + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced QtJson value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.isObject()) { + const QJsonObject &object = m_value.toObject(); + result = object.size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.isString()) { + result = m_value.toString().toStdString(); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.isArray(); + } + + bool isBool() const + { + return m_value.isBool(); + } + + bool isDouble() const + { + return m_value.isDouble(); + } + + bool isInteger() const + { + //toInt returns the default value (0, 1) if the value is not a whole number + return m_value.isDouble() && (m_value.toInt(0) == m_value.toInt(1)); + } + + bool isNull() const + { + return m_value.isNull(); + } + + bool isNumber() const + { + return m_value.isDouble(); + } + + bool isObject() const + { + return m_value.isObject(); + } + + bool isString() const + { + return m_value.isString(); + } + +private: + + /// Return a reference to an empty object singleton + static QJsonValue emptyObject() + { + static const QJsonValue object; + return object; + } + + /// Reference to the contained QtJson value. + const QJsonValue m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting QtJson. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +class QtJsonAdapter: + public BasicAdapter +{ +public: + + /// Construct a QtJsonAdapter that contains an empty object + QtJsonAdapter() + : BasicAdapter() { } + + /// Construct a QtJsonAdapter containing a specific QtJson value + QtJsonAdapter(const QJsonValue &value) + : BasicAdapter(value) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * QtJsonAdapter representing a value stored in the array. + * + * @see QtJsonArray + */ +class QtJsonArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = QtJsonAdapter; + using difference_type = QtJsonAdapter; + using pointer = QtJsonAdapter*; + using reference = QtJsonAdapter&; + + /** + * @brief Construct a new QtJsonArrayValueIterator using an existing + * QtJson iterator. + * + * @param itr QtJson iterator to store + */ + QtJsonArrayValueIterator(const QJsonArray::const_iterator &itr) + : m_itr(itr) { } + + /// Returns a QtJsonAdapter that contains the value of the current + /// element. + QtJsonAdapter operator*() const + { + return QtJsonAdapter(*m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const QtJsonArrayValueIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const QtJsonArrayValueIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const QtJsonArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + QtJsonArrayValueIterator operator++(int) + { + QtJsonArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const QtJsonArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + m_itr += n; + } + +private: + + QJsonArray::const_iterator m_itr; +}; + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of QtJsonObjectMember representing one of the members of the object. It + * has been implemented using the boost iterator_facade template. + * + * @see QtJsonObject + * @see QtJsonObjectMember + */ +class QtJsonObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = QtJsonObjectMember; + using difference_type = QtJsonObjectMember; + using pointer = QtJsonObjectMember*; + using reference = QtJsonObjectMember&; + + /** + * @brief Construct an iterator from a QtJson iterator. + * + * @param itr QtJson iterator to store + */ + QtJsonObjectMemberIterator(const QJsonObject::const_iterator &itr) + : m_itr(itr) { } + + /** + * @brief Returns a QtJsonObjectMember that contains the key and value + * belonging to the object member identified by the iterator. + */ + QtJsonObjectMember operator*() const + { + std::string key = m_itr.key().toStdString(); + return QtJsonObjectMember(key, m_itr.value()); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const QtJsonObjectMemberIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const QtJsonObjectMemberIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const QtJsonObjectMemberIterator& operator++() + { + m_itr++; + return *this; + } + + QtJsonObjectMemberIterator operator++(int) + { + QtJsonObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const QtJsonObjectMemberIterator& operator--(int) + { + m_itr--; + + return *this; + } + +private: + + /// Iternal copy of the original QtJson iterator + QJsonObject::const_iterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for QtJsonAdapter. +template<> +struct AdapterTraits +{ + typedef QJsonValue DocumentType; + + static std::string adapterName() + { + return "QtJsonAdapter"; + } +}; + +inline bool QtJsonFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return QtJsonAdapter(m_value).equalTo(other, strict); +} + +inline QtJsonArrayValueIterator QtJsonArray::begin() const +{ + return m_value.begin(); +} + +inline QtJsonArrayValueIterator QtJsonArray::end() const +{ + return m_value.end(); +} + +inline QtJsonObjectMemberIterator QtJsonObject::begin() const +{ + return m_value.begin(); +} + +inline QtJsonObjectMemberIterator QtJsonObject::end() const +{ + return m_value.end(); +} + +inline QtJsonObjectMemberIterator QtJsonObject::find( + const std::string &propertyName) const +{ + return m_value.find(QString::fromStdString(propertyName)); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/rapidjson_adapter.hpp b/subprojects/valijson/include/valijson/adapters/rapidjson_adapter.hpp new file mode 100644 index 000000000..c9ec75dcc --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/rapidjson_adapter.hpp @@ -0,0 +1,937 @@ +/** + * @file + * + * @brief Adapter implementation for the RapidJson parser library. + * + * Include this file in your program to enable support for RapidJson. + * + * This file defines the following template classes (not in this order): + * - GenericRapidJsonAdapter + * - GenericRapidJsonArray + * - GenericRapidJsonArrayValueIterator + * - GenericRapidJsonFrozenValue + * - GenericRapidJsonObject + * - GenericRapidJsonObjectMember + * - GenericRapidJsonObjectMemberIterator + * - GenericRapidJsonValue + * + * All of these classes share a template argument called 'ValueType', which can + * be used to choose the underlying the RapidJson value type that is used. This + * allows different RapidJson encodings and allocators to be used. + * + * This file also defines the following typedefs, which use RapidJson's default + * ValueType: + * - RapidJsonAdapter + * - RapidJsonArray + * - RapidJsonArrayValueIterator + * - RapidJsonFrozenValue + * - RapidJsonObject + * - RapidJsonObjectMember + * - RapidJsonObjectMemberIterator + * - RapidJsonValue + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is RapidJsonAdapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include +#include + +#ifdef VALIJSON_USE_EXCEPTIONS + #ifdef RAPIDJSON_ASSERT + #warning "RAPIDJSON_ASSERT already defined." + #warning "Please include valijson/adapters/rapidjson_adapter.hpp before any RapidJSON headers." + #else + template + T rapidjson_assert(T t, const std::string& file, const int line) { + if (t) { + return t; + } + + throw std::runtime_error("assertion failed; file: " + file + "; line: " + std::to_string(line)); + } + + #define RAPIDJSON_ASSERT(x) rapidjson_assert(x, __FILE__, __LINE__) + #endif +#endif + +#include + +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +template +class GenericRapidJsonAdapter; + +template +class GenericRapidJsonArrayValueIterator; + +template +class GenericRapidJsonObjectMemberIterator; + +/// Container for a property name and an associated RapidJson value +template +class GenericRapidJsonObjectMember : + public std::pair> +{ +private: + typedef std::pair> Super; + +public: + GenericRapidJsonObjectMember( + const std::string &name, + const GenericRapidJsonAdapter &value) + : Super(name, value) { } +}; + +/** + * @brief Light weight wrapper for a RapidJson array value. + * + * This class is light weight wrapper for a RapidJson array. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to an underlying + * RapidJson value, assumed to be an array, so there is very little overhead + * associated with copy construction and passing by value. + */ +template +class GenericRapidJsonArray +{ +public: + + typedef GenericRapidJsonArrayValueIterator const_iterator; + typedef GenericRapidJsonArrayValueIterator iterator; + + /// Construct a RapidJsonArray referencing an empty array singleton. + GenericRapidJsonArray() + : m_value(emptyArray()) { } + + /** + * @brief Construct a RapidJsonArray referencing a specific RapidJson + * value. + * + * @param value reference to a RapidJson value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + GenericRapidJsonArray(const ValueType &value) + : m_value(value) + { + if (!value.IsArray()) { + throwRuntimeError("Value is not an array."); + } + } + + /// Return an iterator for the first element in the array. + iterator begin() const; + + /// Return an iterator for one-past the last element of the array. + iterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + return m_value.Size(); + } + +private: + + /** + * @brief Return a reference to a RapidJson value that is an empty array. + * + * Note that the value returned by this function is a singleton. + */ + static const ValueType & emptyArray() + { + static const ValueType array(rapidjson::kArrayType); + return array; + } + + /// Reference to the contained value + const ValueType &m_value; +}; + +/** + * @brief Light weight wrapper for a RapidJson object. + * + * This class is light weight wrapper for a RapidJson object. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * RapidJson value, assumed to be an object, so there is very little overhead + * associated with copy construction and passing by value. + */ +template +class GenericRapidJsonObject +{ +public: + + typedef GenericRapidJsonObjectMemberIterator const_iterator; + typedef GenericRapidJsonObjectMemberIterator iterator; + + /// Construct a GenericRapidJsonObject referencing an empty object singleton. + GenericRapidJsonObject() + : m_value(emptyObject()) { } + + /** + * @brief Construct a GenericRapidJsonObject referencing a specific + * RapidJson value. + * + * @param value reference to a RapidJson value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + GenericRapidJsonObject(const ValueType &value) + : m_value(value) + { + if (!value.IsObject()) { + throwRuntimeError("Value is not an object."); + } + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the pointer value returned by the underlying RapidJson implementation. + */ + iterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the pointer value returned by the underlying RapidJson implementation. + */ + iterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param property property name to search for + */ + iterator find(const std::string &property) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + return m_value.MemberEnd() - m_value.MemberBegin(); + } + +private: + + /** + * @brief Return a reference to a RapidJson value that is empty object. + * + * Note that the value returned by this function is a singleton. + */ + static const ValueType & emptyObject() + { + static ValueType object(rapidjson::kObjectType); + return object; + } + + /// Reference to the contained object + const ValueType &m_value; +}; + +/** + * @brief Stores an independent copy of a RapidJson value. + * + * This class allows a RapidJson value to be stored independent of its original + * document. RapidJson makes this a bit harder than usual, because RapidJson + * values are associated with a custom memory allocator. As such, RapidJson + * values have to be copied recursively, referencing a custom allocator held + * by this class. + * + * @see FrozenValue + */ +template +class GenericRapidJsonFrozenValue: public FrozenValue +{ +public: + + explicit GenericRapidJsonFrozenValue(const char *str) + { + m_value.SetString(str, m_allocator); + } + + explicit GenericRapidJsonFrozenValue(const std::string &str) + { + m_value.SetString(str.c_str(), (unsigned int)str.length(), m_allocator); + } + + /** + * @brief Make a copy of a RapidJson value + * + * @param source the RapidJson value to be copied + */ + explicit GenericRapidJsonFrozenValue(const ValueType &source) + { + if (!copy(source, m_value, m_allocator)) { + throwRuntimeError("Failed to copy ValueType"); + } + } + + FrozenValue * clone() const override + { + return new GenericRapidJsonFrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /** + * @brief Recursively copy a RapidJson value using a separate allocator + * + * @param source value to copy from + * @param dest value to copy into + * @param allocator reference to an allocator held by this class + * + * @tparam Allocator type of RapidJson Allocator to be used + * + * @returns true if copied successfully, false otherwise. + */ + template + static bool copy(const ValueType &source, + ValueType &dest, + Allocator &allocator) + { + switch (source.GetType()) { + case rapidjson::kNullType: + dest.SetNull(); + return true; + case rapidjson::kFalseType: + dest.SetBool(false); + return true; + case rapidjson::kTrueType: + dest.SetBool(true); + return true; + case rapidjson::kObjectType: + dest.SetObject(); + for (typename ValueType::ConstMemberIterator itr = source.MemberBegin(); itr != source.MemberEnd(); ++itr) { + ValueType name(itr->name.GetString(), itr->name.GetStringLength(), allocator); + ValueType value; + copy(itr->value, value, allocator); + dest.AddMember(name, value, allocator); + } + return true; + case rapidjson::kArrayType: + dest.SetArray(); + for (typename ValueType::ConstValueIterator itr = source.Begin(); itr != source.End(); ++itr) { + ValueType value; + copy(*itr, value, allocator); + dest.PushBack(value, allocator); + } + return true; + case rapidjson::kStringType: + dest.SetString(source.GetString(), source.GetStringLength(), allocator); + return true; + case rapidjson::kNumberType: + if (source.IsInt()) { + dest.SetInt(source.GetInt()); + } else if (source.IsUint()) { + dest.SetUint(source.GetUint()); + } else if (source.IsInt64()) { + dest.SetInt64(source.GetInt64()); + } else if (source.IsUint64()) { + dest.SetUint64(source.GetUint64()); + } else { + dest.SetDouble(source.GetDouble()); + } + return true; + default: + break; + } + + return false; + } + + /// Local memory allocator for RapidJson value + typename ValueType::AllocatorType m_allocator; + + /// Local RapidJson value + ValueType m_value; +}; + +/** + * @brief Light weight wrapper for a RapidJson value. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a RapidJson value. This class is responsible + * for the mechanics of actually reading a RapidJson value, whereas the + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +template +class GenericRapidJsonValue +{ +public: + /// Construct a wrapper for the empty object singleton + GenericRapidJsonValue() + : m_value(emptyObject()) { } + + /// Construct a wrapper for a specific RapidJson value + GenericRapidJsonValue(const ValueType &value) + : m_value(value) { } + + /** + * @brief Create a new GenericRapidJsonFrozenValue instance that contains + * the value referenced by this GenericRapidJsonValue instance. + * + * @returns pointer to a new GenericRapidJsonFrozenValue instance, belonging + * to the caller. + */ + FrozenValue * freeze() const + { + return new GenericRapidJsonFrozenValue(m_value); + } + + /** + * @brief Optionally return a GenericRapidJsonArray instance. + * + * If the referenced RapidJson value is an array, this function will return + * a std::optional containing a GenericRapidJsonArray instance referencing + * the array. + * + * Otherwise it will return an empty optional. + */ + opt::optional> getArrayOptional() const + { + if (m_value.IsArray()) { + return opt::make_optional(GenericRapidJsonArray(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced RapidJson value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.IsArray()) { + result = m_value.Size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.IsBool()) { + result = m_value.GetBool(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.IsDouble()) { + result = m_value.GetDouble(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if (m_value.IsInt()) { + result = m_value.GetInt(); + return true; + } else if (m_value.IsInt64()) { + result = m_value.GetInt64(); + return true; + } else if (m_value.IsUint()) { + result = static_cast(m_value.GetUint()); + return true; + } else if (m_value.IsUint64()) { + result = static_cast(m_value.GetUint64()); + return true; + } + + return false; + } + + /** + * @brief Optionally return a GenericRapidJsonObject instance. + * + * If the referenced RapidJson value is an object, this function will return + * a std::optional containing a GenericRapidJsonObject instance + * referencing the object. + * + * Otherwise it will return an empty optional. + */ + opt::optional> getObjectOptional() const + { + if (m_value.IsObject()) { + return opt::make_optional(GenericRapidJsonObject(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced RapidJson value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.IsObject()) { + result = m_value.MemberEnd() - m_value.MemberBegin(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.IsString()) { + result.assign(m_value.GetString(), m_value.GetStringLength()); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.IsArray(); + } + + bool isBool() const + { + return m_value.IsBool(); + } + + bool isDouble() const + { + return m_value.IsDouble(); + } + + bool isInteger() const + { + return m_value.IsInt() || m_value.IsInt64() || m_value.IsUint() || m_value.IsUint64(); + } + + bool isNull() const + { + return m_value.IsNull(); + } + + bool isNumber() const + { + return m_value.IsNumber(); + } + + bool isObject() const + { + return m_value.IsObject(); + } + + bool isString() const + { + return m_value.IsString(); + } + +private: + + /// Return a reference to an empty object singleton + static const ValueType & emptyObject() + { + static const ValueType object(rapidjson::kObjectType); + return object; + } + + /// Reference to the contained RapidJson value. + const ValueType &m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting RapidJson. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +template +class GenericRapidJsonAdapter: + public BasicAdapter, + GenericRapidJsonArray, + GenericRapidJsonObjectMember, + GenericRapidJsonObject, + GenericRapidJsonValue> +{ +public: + + /// Construct a RapidJsonAdapter that contains an empty object + GenericRapidJsonAdapter() + : BasicAdapter, + GenericRapidJsonArray, + GenericRapidJsonObjectMember, + GenericRapidJsonObject, + GenericRapidJsonValue>() { } + + /// Construct a RapidJsonAdapter containing a specific RapidJson value + GenericRapidJsonAdapter(const ValueType &value) + : BasicAdapter, + GenericRapidJsonArray, + GenericRapidJsonObjectMember, + GenericRapidJsonObject, + GenericRapidJsonValue>(value) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * RapidJsonAdapter representing a value stored in the array. + * + * @see RapidJsonArray + */ +template +class GenericRapidJsonArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = GenericRapidJsonAdapter; + using difference_type = GenericRapidJsonAdapter; + using pointer = GenericRapidJsonAdapter*; + using reference = GenericRapidJsonAdapter&; + + /** + * @brief Construct a new GenericRapidJsonArrayValueIterator using an + * existing RapidJson iterator. + * + * @param itr RapidJson iterator to store + */ + GenericRapidJsonArrayValueIterator( + const typename ValueType::ConstValueIterator &itr) + : m_itr(itr) { } + + /// Returns a GenericRapidJsonAdapter that contains the value of the current + /// element. + GenericRapidJsonAdapter operator*() const + { + return GenericRapidJsonAdapter(*m_itr); + } + + /// Returns a proxy for the value of the current element + DerefProxy> operator->() const + { + return DerefProxy>(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const GenericRapidJsonArrayValueIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const GenericRapidJsonArrayValueIterator& other) const + { + return m_itr != other.m_itr; + } + + GenericRapidJsonArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + GenericRapidJsonArrayValueIterator operator++(int) { + GenericRapidJsonArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + GenericRapidJsonArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + m_itr += n; + } + + std::ptrdiff_t difference(const GenericRapidJsonArrayValueIterator &other) + { + return std::distance(m_itr, other.itr); + } + +private: + + typename ValueType::ConstValueIterator m_itr; +}; + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of GenericRapidJsonObjectMember representing one of the members of the + * object. + * + * @see GenericRapidJsonObject + * @see GenericRapidJsonObjectMember + */ +template +class GenericRapidJsonObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = GenericRapidJsonObjectMember; + using difference_type = GenericRapidJsonObjectMember; + using pointer = GenericRapidJsonObjectMember*; + using reference = GenericRapidJsonObjectMember&; + + /** + * @brief Construct an iterator from a RapidJson iterator. + * + * @param itr RapidJson iterator to store + */ + GenericRapidJsonObjectMemberIterator( + const typename ValueType::ConstMemberIterator &itr) + : m_itr(itr) { } + + + /** + * @brief Returns a GenericRapidJsonObjectMember that contains the key and + * value belonging to the object member identified by the iterator. + */ + GenericRapidJsonObjectMember operator*() const + { + return GenericRapidJsonObjectMember( + std::string(m_itr->name.GetString(), m_itr->name.GetStringLength()), + m_itr->value); + } + + /// Returns a proxy for the value of the current element + DerefProxy> operator->() const + { + return DerefProxy>(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const GenericRapidJsonObjectMemberIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const GenericRapidJsonObjectMemberIterator &other) const + { + return m_itr != other.m_itr; + } + + GenericRapidJsonObjectMemberIterator& operator++() + { + m_itr++; + return *this; + } + + GenericRapidJsonObjectMemberIterator operator++(int) + { + GenericRapidJsonObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + GenericRapidJsonObjectMemberIterator& operator--() + { + m_itr--; + return *this; + } + + std::ptrdiff_t difference(const GenericRapidJsonObjectMemberIterator &other) + { + return std::distance(m_itr, other.itr); + } + +private: + + /// Iternal copy of the original RapidJson iterator + typename ValueType::ConstMemberIterator m_itr; +}; + +template +inline bool GenericRapidJsonFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return GenericRapidJsonAdapter(m_value).equalTo(other, strict); +} + +template +inline typename GenericRapidJsonArray::iterator GenericRapidJsonArray::begin() const +{ + return m_value.Begin(); +} + +template +inline typename GenericRapidJsonArray::iterator GenericRapidJsonArray::end() const +{ + return m_value.End(); +} + +template +inline typename GenericRapidJsonObject::iterator GenericRapidJsonObject::begin() const +{ + return m_value.MemberBegin(); +} + +template +inline typename GenericRapidJsonObject::iterator GenericRapidJsonObject::end() const +{ + return m_value.MemberEnd(); +} + +template +inline typename GenericRapidJsonObject::iterator + GenericRapidJsonObject::find(const std::string &propertyName) const +{ + // Hack to support older versions of rapidjson where pointers are used as + // the built in iterator type. In those versions, the FindMember function + // would return a null pointer when the requested member could not be + // found. After calling FindMember on an empty object, we compare the + // result against what we would expect if a non-null-pointer iterator was + // returned. + const ValueType empty(rapidjson::kObjectType); + const typename ValueType::ConstMemberIterator maybeEnd = empty.FindMember(""); + if (maybeEnd != empty.MemberBegin() + 1) { + // In addition to the pointer-based iterator issue, RapidJson's internal + // string comparison code seemed to rely on the query string being + // initialised to a length greater than or equal to that of the + // properties being compared. We get around this by implementing our + // own linear scan. + const size_t propertyNameLength = propertyName.length(); + for (typename ValueType::ConstMemberIterator itr = m_value.MemberBegin(); itr != m_value.MemberEnd(); ++itr) { + const size_t memberNameLength = itr->name.GetStringLength(); + if (memberNameLength == propertyNameLength && + strncmp(itr->name.GetString(), propertyName.c_str(), itr->name.GetStringLength()) == 0) { + return itr; + } + } + + return m_value.MemberEnd(); + } + + return m_value.FindMember(propertyName.c_str()); // Times are good. +} + +typedef GenericRapidJsonAdapter<> RapidJsonAdapter; +typedef GenericRapidJsonArray<> RapidJsonArray; +typedef GenericRapidJsonArrayValueIterator<> RapidJsonArrayValue; +typedef GenericRapidJsonFrozenValue<> RapidJsonFrozenValue; +typedef GenericRapidJsonObject<> RapidJsonObject; +typedef GenericRapidJsonObjectMember<> RapidJsonObjectMember; +typedef GenericRapidJsonObjectMemberIterator<> RapidJsonObjectMemberIterator; +typedef GenericRapidJsonValue<> RapidJsonValue; + +/** + * @brief Specialisation of the AdapterTraits template struct for a + * RapidJsonAdapter that uses a pool allocator + */ +template<> +struct AdapterTraits +{ + typedef rapidjson::Document DocumentType; + + static std::string adapterName() + { + return "RapidJsonAdapter"; + } +}; + +typedef rapidjson::GenericValue, rapidjson::CrtAllocator> RapidJsonCrt; + +/** + * @brief Specialisation of the AdapterTraits template struct for a + * RapidJsonAdapter that uses the default CRT allocator + */ +template<> +struct AdapterTraits> +{ + typedef rapidjson::GenericDocument, rapidjson::CrtAllocator> DocumentType; + + static std::string adapterName() + { + return "GenericRapidJsonAdapter (using CrtAllocator)"; + } +}; + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/adapters/std_string_adapter.hpp b/subprojects/valijson/include/valijson/adapters/std_string_adapter.hpp new file mode 100644 index 000000000..2084024f2 --- /dev/null +++ b/subprojects/valijson/include/valijson/adapters/std_string_adapter.hpp @@ -0,0 +1,491 @@ +/** + * @file + * + * @brief Adapter implementation that wraps a single std::string value + * + * This allows property names to be validated against a schema as though they are a generic JSON + * value, while allowing the rest of Valijson's API to expose property names as plain std::string + * values. + * + * This was added while implementing draft 7 support. This included support for a constraint + * called propertyNames, which can be used to ensure that the property names in an object + * validate against a subschema. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace valijson { +namespace adapters { + +class StdStringAdapter; +class StdStringArrayValueIterator; +class StdStringObjectMemberIterator; + +typedef std::pair StdStringObjectMember; + +class StdStringArray +{ +public: + typedef StdStringArrayValueIterator const_iterator; + typedef StdStringArrayValueIterator iterator; + + StdStringArray() = default; + + StdStringArrayValueIterator begin() const; + + StdStringArrayValueIterator end() const; + + static size_t size() + { + return 0; + } +}; + +class StdStringObject +{ +public: + typedef StdStringObjectMemberIterator const_iterator; + typedef StdStringObjectMemberIterator iterator; + + StdStringObject() = default; + + StdStringObjectMemberIterator begin() const; + + StdStringObjectMemberIterator end() const; + + StdStringObjectMemberIterator find(const std::string &propertyName) const; + + static size_t size() + { + return 0; + } +}; + +class StdStringFrozenValue: public FrozenValue +{ +public: + explicit StdStringFrozenValue(std::string source) + : value(std::move(source)) { } + + FrozenValue * clone() const override + { + return new StdStringFrozenValue(value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + std::string value; +}; + +class StdStringAdapter: public Adapter +{ +public: + typedef StdStringArray Array; + typedef StdStringObject Object; + typedef StdStringObjectMember ObjectMember; + + explicit StdStringAdapter(const std::string &value) + : m_value(value) { } + + bool applyToArray(ArrayValueCallback) const override + { + return maybeArray(); + } + + bool applyToObject(ObjectMemberCallback) const override + { + return maybeObject(); + } + + StdStringArray asArray() const + { + if (maybeArray()) { + return {}; + } + + throwRuntimeError("String value cannot be cast to array"); + } + + bool asBool() const override + { + return true; + } + + bool asBool(bool &result) const override + { + result = true; + return true; + } + + double asDouble() const override + { + return 0; + } + + bool asDouble(double &result) const override + { + result = 0; + return true; + } + + int64_t asInteger() const override + { + return 0; + } + + bool asInteger(int64_t &result) const override + { + result = 0; + return true; + }; + + StdStringObject asObject() const + { + if (maybeObject()) { + return {}; + } + + throwRuntimeError("String value cannot be cast to object"); + } + + std::string asString() const override + { + return m_value; + } + + bool asString(std::string &result) const override + { + result = m_value; + return true; + } + + bool equalTo(const Adapter &other, bool strict) const override + { + if (strict && !other.isString()) { + return false; + } + + return m_value == other.asString(); + } + + FrozenValue* freeze() const override + { + return new StdStringFrozenValue(m_value); + } + + static StdStringArray getArray() + { + throwNotSupported(); + } + + size_t getArraySize() const override + { + throwNotSupported(); + } + + bool getArraySize(size_t &) const override + { + throwNotSupported(); + } + + bool getBool() const override + { + throwNotSupported(); + } + + bool getBool(bool &) const override + { + throwNotSupported(); + } + + double getDouble() const override + { + throwNotSupported(); + } + + bool getDouble(double &) const override + { + throwNotSupported(); + } + + int64_t getInteger() const override + { + throwNotSupported(); + } + + bool getInteger(int64_t &) const override + { + throwNotSupported(); + } + + double getNumber() const override + { + throwNotSupported(); + } + + bool getNumber(double &) const override + { + throwNotSupported(); + } + + size_t getObjectSize() const override + { + throwNotSupported(); + } + + bool getObjectSize(size_t &) const override + { + throwNotSupported(); + } + + std::string getString() const override + { + return m_value; + } + + bool getString(std::string &result) const override + { + result = m_value; + return true; + } + + bool hasStrictTypes() const override + { + return true; + } + + bool isArray() const override + { + return false; + } + + bool isBool() const override + { + return false; + } + + bool isDouble() const override + { + return false; + } + + bool isInteger() const override + { + return false; + } + + bool isNull() const override + { + return false; + } + + bool isNumber() const override + { + return false; + } + + bool isObject() const override + { + return false; + } + + bool isString() const override + { + return true; + } + + bool maybeArray() const override + { + return false; + } + + bool maybeBool() const override + { + return m_value == "true" || m_value == "false"; + } + + bool maybeDouble() const override + { + const char *b = m_value.c_str(); + char *e = nullptr; + strtod(b, &e); + return e != b && e == b + m_value.length(); + } + + bool maybeInteger() const override + { + std::istringstream i(m_value); + int64_t x; + char c; + if (!(i >> x) || i.get(c)) { + return false; + } + + return true; + } + + bool maybeNull() const override + { + return m_value.empty(); + } + + bool maybeObject() const override + { + return m_value.empty(); + } + + bool maybeString() const override + { + return true; + } + +private: + const std::string &m_value; +}; + +class StdStringArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = StdStringAdapter; + using difference_type = StdStringAdapter; + using pointer = StdStringAdapter*; + using reference = StdStringAdapter&; + + StdStringAdapter operator*() const + { + throwNotSupported(); + } + + DerefProxy operator->() const + { + throwNotSupported(); + } + + bool operator==(const StdStringArrayValueIterator &) const + { + return true; + } + + bool operator!=(const StdStringArrayValueIterator &) const + { + return false; + } + + const StdStringArrayValueIterator& operator++() + { + throwNotSupported(); + } + + StdStringArrayValueIterator operator++(int) + { + throwNotSupported(); + } + + const StdStringArrayValueIterator& operator--() + { + throwNotSupported(); + } + + void advance(std::ptrdiff_t) + { + throwNotSupported(); + } +}; + +inline StdStringArrayValueIterator StdStringArray::begin() const +{ + return {}; +} + +inline StdStringArrayValueIterator StdStringArray::end() const +{ + return {}; +} + +class StdStringObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = StdStringObjectMember; + using difference_type = StdStringObjectMember; + using pointer = StdStringObjectMember*; + using reference = StdStringObjectMember&; + + StdStringObjectMember operator*() const + { + throwNotSupported(); + } + + DerefProxy operator->() const + { + throwNotSupported(); + } + + bool operator==(const StdStringObjectMemberIterator &) const + { + return true; + } + + bool operator!=(const StdStringObjectMemberIterator &) const + { + return false; + } + + const StdStringObjectMemberIterator& operator++() + { + throwNotSupported(); + } + + StdStringObjectMemberIterator operator++(int) + { + throwNotSupported(); + } + + const StdStringObjectMemberIterator& operator--() + { + throwNotSupported(); + } +}; + +inline StdStringObjectMemberIterator StdStringObject::begin() const +{ + return {}; +} + +inline StdStringObjectMemberIterator StdStringObject::end() const +{ + return {}; +} + +inline StdStringObjectMemberIterator StdStringObject::find(const std::string &) const +{ + return {}; +} + +template<> +struct AdapterTraits +{ + typedef std::string DocumentType; + + static std::string adapterName() + { + return "StdStringAdapter"; + } +}; + +inline bool StdStringFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return StdStringAdapter(value).equalTo(other, strict); +} + +} // namespace adapters +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/constraint_builder.hpp b/subprojects/valijson/include/valijson/constraint_builder.hpp new file mode 100644 index 000000000..53407a95e --- /dev/null +++ b/subprojects/valijson/include/valijson/constraint_builder.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace valijson { + +namespace adapters { + class Adapter; +} + +namespace constraints { + struct Constraint; +} + +class ConstraintBuilder +{ +public: + virtual ~ConstraintBuilder() = default; + + virtual constraints::Constraint * make(const adapters::Adapter &) const = 0; +}; + +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/constraints/basic_constraint.hpp b/subprojects/valijson/include/valijson/constraints/basic_constraint.hpp new file mode 100644 index 000000000..5de968318 --- /dev/null +++ b/subprojects/valijson/include/valijson/constraints/basic_constraint.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include + +namespace valijson { +namespace constraints { + +/** + * @brief Template class that implements the accept() and clone() functions of the Constraint interface. + * + * @tparam ConstraintType name of the concrete constraint type, which must provide a copy constructor. + */ +template +struct BasicConstraint: Constraint +{ + typedef internal::CustomAllocator Allocator; + + typedef std::basic_string, internal::CustomAllocator> String; + + BasicConstraint() + : m_allocator() { } + + BasicConstraint(Allocator::CustomAlloc allocFn, Allocator::CustomFree freeFn) + : m_allocator(allocFn, freeFn) { } + + BasicConstraint(const BasicConstraint &other) + : m_allocator(other.m_allocator) { } + + ~BasicConstraint() override = default; + + bool accept(ConstraintVisitor &visitor) const override + { + return visitor.visit(*static_cast(this)); + } + + Constraint * clone(CustomAlloc allocFn, CustomFree) const override + { + void *ptr = allocFn(sizeof(ConstraintType)); + if (!ptr) { + throwRuntimeError("Failed to allocate memory for cloned constraint"); + } + + return new (ptr) ConstraintType(*static_cast(this)); + } + +protected: + + Allocator m_allocator; +}; + +} // namespace constraints +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/constraints/concrete_constraints.hpp b/subprojects/valijson/include/valijson/constraints/concrete_constraints.hpp new file mode 100644 index 000000000..55cc1248a --- /dev/null +++ b/subprojects/valijson/include/valijson/constraints/concrete_constraints.hpp @@ -0,0 +1,1247 @@ +/** + * @file + * + * @brief Class definitions to support JSON Schema constraints + * + * This file contains class definitions for all of the constraints required to + * support JSON Schema. These classes all inherit from the BasicConstraint + * template class, which implements the common parts of the Constraint + * interface. + * + * @see BasicConstraint + * @see Constraint + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4702 ) +#endif + +namespace valijson { + +class ValidationResults; + +namespace constraints { + +/** + * @brief Represents an 'allOf' constraint. + * + * An allOf constraint provides a collection of sub-schemas that a value must + * validate against. If a value fails to validate against any of these sub- + * schemas, then validation fails. + */ +class AllOfConstraint: public BasicConstraint +{ +public: + AllOfConstraint() + : m_subschemas(Allocator::rebind::other(m_allocator)) { } + + AllOfConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_subschemas(Allocator::rebind::other(m_allocator)) { } + + void addSubschema(const Subschema *subschema) + { + m_subschemas.push_back(subschema); + } + + template + void applyToSubschemas(const FunctorType &fn) const + { + unsigned int index = 0; + for (const Subschema *subschema : m_subschemas) { + if (!fn(index, subschema)) { + return; + } + + index++; + } + } + +private: + typedef std::vector> Subschemas; + + /// Collection of sub-schemas, all of which must be satisfied + Subschemas m_subschemas; +}; + +/** + * @brief Represents an 'anyOf' constraint + * + * An anyOf constraint provides a collection of sub-schemas that a value can + * validate against. If a value validates against one of these sub-schemas, + * then the validation passes. + */ +class AnyOfConstraint: public BasicConstraint +{ +public: + AnyOfConstraint() + : m_subschemas(Allocator::rebind::other(m_allocator)) { } + + AnyOfConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_subschemas(Allocator::rebind::other(m_allocator)) { } + + void addSubschema(const Subschema *subschema) + { + m_subschemas.push_back(subschema); + } + + template + void applyToSubschemas(const FunctorType &fn) const + { + unsigned int index = 0; + for (const Subschema *subschema : m_subschemas) { + if (!fn(index, subschema)) { + return; + } + + index++; + } + } + +private: + typedef std::vector> Subschemas; + + /// Collection of sub-schemas, at least one of which must be satisfied + Subschemas m_subschemas; +}; + +/** + * @brief Represents a combination 'if', 'then' and 'else' constraints + * + * The schema provided by an 'if' constraint is used as the expression for a conditional. When the + * target validates against that schema, the 'then' subschema will be also be tested. Otherwise, + * the 'else' subschema will be tested. + */ +class ConditionalConstraint: public BasicConstraint +{ +public: + ConditionalConstraint() + : m_ifSubschema(nullptr), + m_thenSubschema(nullptr), + m_elseSubschema(nullptr) { } + + ConditionalConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_ifSubschema(nullptr), + m_thenSubschema(nullptr), + m_elseSubschema(nullptr) { } + + const Subschema * getIfSubschema() const + { + return m_ifSubschema; + } + + const Subschema * getThenSubschema() const + { + return m_thenSubschema; + } + + const Subschema * getElseSubschema() const + { + return m_elseSubschema; + } + + void setIfSubschema(const Subschema *subschema) + { + m_ifSubschema = subschema; + } + + void setThenSubschema(const Subschema *subschema) + { + m_thenSubschema = subschema; + } + + void setElseSubschema(const Subschema *subschema) + { + m_elseSubschema = subschema; + } + +private: + const Subschema *m_ifSubschema; + const Subschema *m_thenSubschema; + const Subschema *m_elseSubschema; +}; + +class ConstConstraint: public BasicConstraint +{ +public: + ConstConstraint() + : m_value(nullptr) { } + + ConstConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_value(nullptr) { } + + ConstConstraint(const ConstConstraint &other) + : BasicConstraint(other), + m_value(other.m_value->clone()) { } + + adapters::FrozenValue * getValue() const + { + return m_value.get(); + } + + void setValue(const adapters::Adapter &value) + { + m_value = std::unique_ptr(value.freeze()); + } + +private: + std::unique_ptr m_value; +}; + +/** + * @brief Represents a 'contains' constraint + * + * A 'contains' constraint specifies a schema that must be satisfied by at least one + * of the values in an array. + */ +class ContainsConstraint: public BasicConstraint +{ +public: + ContainsConstraint() + : m_subschema(nullptr) { } + + ContainsConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_subschema(nullptr) { } + + const Subschema * getSubschema() const + { + return m_subschema; + } + + void setSubschema(const Subschema *subschema) + { + m_subschema = subschema; + } + +private: + const Subschema *m_subschema; +}; + +/** + * @brief Represents a 'dependencies' constraint. + * + * A dependency constraint ensures that a given property is valid only if the + * properties that it depends on are present. + */ +class DependenciesConstraint: public BasicConstraint +{ +public: + DependenciesConstraint() + : m_propertyDependencies(std::less(), m_allocator), + m_schemaDependencies(std::less(), m_allocator) + { } + + DependenciesConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_propertyDependencies(std::less(), m_allocator), + m_schemaDependencies(std::less(), m_allocator) + { } + + template + DependenciesConstraint & addPropertyDependency( + const StringType &propertyName, + const StringType &dependencyName) + { + const String key(propertyName.c_str(), m_allocator); + auto itr = m_propertyDependencies.find(key); + if (itr == m_propertyDependencies.end()) { + itr = m_propertyDependencies.insert(PropertyDependencies::value_type( + key, PropertySet(std::less(), m_allocator))).first; + } + + itr->second.insert(String(dependencyName.c_str(), m_allocator)); + + return *this; + } + + template + DependenciesConstraint & addPropertyDependencies( + const StringType &propertyName, + const ContainerType &dependencyNames) + { + const String key(propertyName.c_str(), m_allocator); + auto itr = m_propertyDependencies.find(key); + if (itr == m_propertyDependencies.end()) { + itr = m_propertyDependencies.insert(PropertyDependencies::value_type( + key, PropertySet(std::less(), m_allocator))).first; + } + + typedef typename ContainerType::value_type ValueType; + for (const ValueType &dependencyName : dependencyNames) { + itr->second.insert(String(dependencyName.c_str(), m_allocator)); + } + + return *this; + } + + template + DependenciesConstraint & addSchemaDependency(const StringType &propertyName, const Subschema *schemaDependency) + { + if (m_schemaDependencies.insert(SchemaDependencies::value_type( + String(propertyName.c_str(), m_allocator), + schemaDependency)).second) { + return *this; + } + + throwRuntimeError("Dependencies constraint already contains a dependent " + "schema for the property '" + propertyName + "'"); + } + + template + void applyToPropertyDependencies(const FunctorType &fn) const + { + for (const PropertyDependencies::value_type &v : m_propertyDependencies) { + if (!fn(v.first, v.second)) { + return; + } + } + } + + template + void applyToSchemaDependencies(const FunctorType &fn) const + { + for (const SchemaDependencies::value_type &v : m_schemaDependencies) { + if (!fn(v.first, v.second)) { + return; + } + } + } + +private: + typedef std::set, internal::CustomAllocator> PropertySet; + + typedef std::map, + internal::CustomAllocator>> PropertyDependencies; + + typedef std::map, + internal::CustomAllocator>> SchemaDependencies; + + /// Mapping from property names to their property-based dependencies + PropertyDependencies m_propertyDependencies; + + /// Mapping from property names to their schema-based dependencies + SchemaDependencies m_schemaDependencies; +}; + +/** + * @brief Represents an 'enum' constraint + * + * An enum constraint provides a collection of permissible values for a JSON + * node. The node will only validate against this constraint if it matches one + * or more of the values in the collection. + */ +class EnumConstraint: public BasicConstraint +{ +public: + EnumConstraint() + : m_enumValues(Allocator::rebind::other(m_allocator)) { } + + EnumConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_enumValues(Allocator::rebind::other(m_allocator)) { } + + EnumConstraint(const EnumConstraint &other) + : BasicConstraint(other), + m_enumValues(Allocator::rebind::other(m_allocator)) + { +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + // Clone individual enum values + for (const EnumValue *otherValue : other.m_enumValues) { + const EnumValue *value = otherValue->clone(); +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + m_enumValues.push_back(value); +#if VALIJSON_USE_EXCEPTIONS + } catch (...) { + delete value; + value = nullptr; + throw; + } + } + } catch (...) { + // Delete values already added to constraint + for (const EnumValue *value : m_enumValues) { + delete value; + } + throw; +#endif + } + } + + ~EnumConstraint() override + { + for (const EnumValue *value : m_enumValues) { + delete value; + } + } + + void addValue(const adapters::Adapter &value) + { + // TODO: Freeze value using custom alloc/free functions + m_enumValues.push_back(value.freeze()); + } + + void addValue(const adapters::FrozenValue &value) + { + // TODO: Clone using custom alloc/free functions + m_enumValues.push_back(value.clone()); + } + + template + void applyToValues(const FunctorType &fn) const + { + for (const EnumValue *value : m_enumValues) { + if (!fn(*value)) { + return; + } + } + } + +private: + typedef adapters::FrozenValue EnumValue; + + typedef std::vector> EnumValues; + + EnumValues m_enumValues; +}; + +/** + * @brief Represents non-singular 'items' and 'additionalItems' constraints + * + * Unlike the SingularItemsConstraint class, this class represents an 'items' + * constraint that specifies an array of sub-schemas, which should be used to + * validate each item in an array, in sequence. It also represents an optional + * 'additionalItems' sub-schema that should be used when an array contains + * more values than there are sub-schemas in the 'items' constraint. + * + * The prefix 'Linear' comes from the fact that this class contains a list of + * sub-schemas that corresponding array items must be validated against, and + * this validation is performed linearly (i.e. in sequence). + */ +class LinearItemsConstraint: public BasicConstraint +{ +public: + LinearItemsConstraint() + : m_itemSubschemas(Allocator::rebind::other(m_allocator)), + m_additionalItemsSubschema(nullptr) { } + + LinearItemsConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_itemSubschemas(Allocator::rebind::other(m_allocator)), + m_additionalItemsSubschema(nullptr) { } + + void addItemSubschema(const Subschema *subschema) + { + m_itemSubschemas.push_back(subschema); + } + + template + void applyToItemSubschemas(const FunctorType &fn) const + { + unsigned int index = 0; + for (const Subschema *subschema : m_itemSubschemas) { + if (!fn(index, subschema)) { + return; + } + + index++; + } + } + + const Subschema * getAdditionalItemsSubschema() const + { + return m_additionalItemsSubschema; + } + + size_t getItemSubschemaCount() const + { + return m_itemSubschemas.size(); + } + + void setAdditionalItemsSubschema(const Subschema *subschema) + { + m_additionalItemsSubschema = subschema; + } + +private: + typedef std::vector> Subschemas; + + Subschemas m_itemSubschemas; + + const Subschema* m_additionalItemsSubschema; +}; + +/** + * @brief Represents 'maximum' and 'exclusiveMaximum' constraints + */ +class MaximumConstraint: public BasicConstraint +{ +public: + MaximumConstraint() + : m_maximum(std::numeric_limits::infinity()), + m_exclusiveMaximum(false) { } + + MaximumConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_maximum(std::numeric_limits::infinity()), + m_exclusiveMaximum(false) { } + + bool getExclusiveMaximum() const + { + return m_exclusiveMaximum; + } + + void setExclusiveMaximum(bool newExclusiveMaximum) + { + m_exclusiveMaximum = newExclusiveMaximum; + } + + double getMaximum() const + { + return m_maximum; + } + + void setMaximum(double newMaximum) + { + m_maximum = newMaximum; + } + +private: + double m_maximum; + bool m_exclusiveMaximum; +}; + +/** + * @brief Represents a 'maxItems' constraint + */ +class MaxItemsConstraint: public BasicConstraint +{ +public: + MaxItemsConstraint() + : m_maxItems(std::numeric_limits::max()) { } + + MaxItemsConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_maxItems(std::numeric_limits::max()) { } + + uint64_t getMaxItems() const + { + return m_maxItems; + } + + void setMaxItems(uint64_t newMaxItems) + { + m_maxItems = newMaxItems; + } + +private: + uint64_t m_maxItems; +}; + +/** + * @brief Represents a 'maxLength' constraint + */ +class MaxLengthConstraint: public BasicConstraint +{ +public: + MaxLengthConstraint() + : m_maxLength(std::numeric_limits::max()) { } + + MaxLengthConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_maxLength(std::numeric_limits::max()) { } + + uint64_t getMaxLength() const + { + return m_maxLength; + } + + void setMaxLength(uint64_t newMaxLength) + { + m_maxLength = newMaxLength; + } + +private: + uint64_t m_maxLength; +}; + +/** + * @brief Represents a 'maxProperties' constraint + */ +class MaxPropertiesConstraint: public BasicConstraint +{ +public: + MaxPropertiesConstraint() + : m_maxProperties(std::numeric_limits::max()) { } + + MaxPropertiesConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_maxProperties(std::numeric_limits::max()) { } + + uint64_t getMaxProperties() const + { + return m_maxProperties; + } + + void setMaxProperties(uint64_t newMaxProperties) + { + m_maxProperties = newMaxProperties; + } + +private: + uint64_t m_maxProperties; +}; + +/** + * @brief Represents 'minimum' and 'exclusiveMinimum' constraints + */ +class MinimumConstraint: public BasicConstraint +{ +public: + MinimumConstraint() + : m_minimum(-std::numeric_limits::infinity()), + m_exclusiveMinimum(false) { } + + MinimumConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_minimum(-std::numeric_limits::infinity()), + m_exclusiveMinimum(false) { } + + bool getExclusiveMinimum() const + { + return m_exclusiveMinimum; + } + + void setExclusiveMinimum(bool newExclusiveMinimum) + { + m_exclusiveMinimum = newExclusiveMinimum; + } + + double getMinimum() const + { + return m_minimum; + } + + void setMinimum(double newMinimum) + { + m_minimum = newMinimum; + } + +private: + double m_minimum; + bool m_exclusiveMinimum; +}; + +/** + * @brief Represents a 'minItems' constraint + */ +class MinItemsConstraint: public BasicConstraint +{ +public: + MinItemsConstraint() + : m_minItems(0) { } + + MinItemsConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_minItems(0) { } + + uint64_t getMinItems() const + { + return m_minItems; + } + + void setMinItems(uint64_t newMinItems) + { + m_minItems = newMinItems; + } + +private: + uint64_t m_minItems; +}; + +/** + * @brief Represents a 'minLength' constraint + */ +class MinLengthConstraint: public BasicConstraint +{ +public: + MinLengthConstraint() + : m_minLength(0) { } + + MinLengthConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_minLength(0) { } + + uint64_t getMinLength() const + { + return m_minLength; + } + + void setMinLength(uint64_t newMinLength) + { + m_minLength = newMinLength; + } + +private: + uint64_t m_minLength; +}; + +/** + * @brief Represents a 'minProperties' constraint + */ +class MinPropertiesConstraint: public BasicConstraint +{ +public: + MinPropertiesConstraint() + : m_minProperties(0) { } + + MinPropertiesConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_minProperties(0) { } + + uint64_t getMinProperties() const + { + return m_minProperties; + } + + void setMinProperties(uint64_t newMinProperties) + { + m_minProperties = newMinProperties; + } + +private: + uint64_t m_minProperties; +}; + +/** + * @brief Represents either 'multipleOf' or 'divisibleBy' constraints where + * the divisor is a floating point number + */ +class MultipleOfDoubleConstraint: + public BasicConstraint +{ +public: + MultipleOfDoubleConstraint() + : m_value(1.) { } + + MultipleOfDoubleConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_value(1.) { } + + double getDivisor() const + { + return m_value; + } + + void setDivisor(double newValue) + { + m_value = newValue; + } + +private: + double m_value; +}; + +/** + * @brief Represents either 'multipleOf' or 'divisibleBy' constraints where + * the divisor is of integer type + */ +class MultipleOfIntConstraint: + public BasicConstraint +{ +public: + MultipleOfIntConstraint() + : m_value(1) { } + + MultipleOfIntConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_value(1) { } + + int64_t getDivisor() const + { + return m_value; + } + + void setDivisor(int64_t newValue) + { + m_value = newValue; + } + +private: + int64_t m_value; +}; + +/** + * @brief Represents a 'not' constraint + */ +class NotConstraint: public BasicConstraint +{ +public: + NotConstraint() + : m_subschema(nullptr) { } + + NotConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_subschema(nullptr) { } + + const Subschema * getSubschema() const + { + return m_subschema; + } + + void setSubschema(const Subschema *subschema) + { + m_subschema = subschema; + } + +private: + const Subschema *m_subschema; +}; + +/** + * @brief Represents a 'oneOf' constraint. + */ +class OneOfConstraint: public BasicConstraint +{ +public: + OneOfConstraint() + : m_subschemas(Allocator::rebind::other(m_allocator)) { } + + OneOfConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_subschemas(Allocator::rebind::other(m_allocator)) { } + + void addSubschema(const Subschema *subschema) + { + m_subschemas.push_back(subschema); + } + + template + void applyToSubschemas(const FunctorType &fn) const + { + unsigned int index = 0; + for (const Subschema *subschema : m_subschemas) { + if (!fn(index, subschema)) { + return; + } + + index++; + } + } + +private: + typedef std::vector> Subschemas; + + /// Collection of sub-schemas, exactly one of which must be satisfied + Subschemas m_subschemas; +}; + +/** + * @brief Represents a 'pattern' constraint + */ +class PatternConstraint: public BasicConstraint +{ +public: + PatternConstraint() + : m_pattern(Allocator::rebind::other(m_allocator)) { } + + PatternConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_pattern(Allocator::rebind::other(m_allocator)) { } + + template + bool getPattern(std::basic_string, AllocatorType> &result) const + { + result.assign(m_pattern.c_str()); + return true; + } + + template + std::basic_string, AllocatorType> getPattern( + const AllocatorType &alloc = AllocatorType()) const + { + return std::basic_string, AllocatorType>(m_pattern.c_str(), alloc); + } + + template + void setPattern(const std::basic_string, AllocatorType> &pattern) + { + m_pattern.assign(pattern.c_str()); + } + +private: + String m_pattern; +}; + +class PolyConstraint : public Constraint +{ +public: + bool accept(ConstraintVisitor &visitor) const override + { + return visitor.visit(*static_cast(this)); + } + + Constraint * clone(CustomAlloc allocFn, CustomFree freeFn) const override + { + void *ptr = allocFn(sizeOf()); + if (!ptr) { + throwRuntimeError("Failed to allocate memory for cloned constraint"); + } + +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + return cloneInto(ptr); +#if VALIJSON_USE_EXCEPTIONS + } catch (...) { + freeFn(ptr); + throw; + } +#else + // pretend to use freeFn to avoid warning in GCC 8.3 + (void)freeFn; +#endif + } + + virtual bool validate(const adapters::Adapter &target, + const std::vector& context, + valijson::ValidationResults *results) const = 0; + +private: + virtual Constraint * cloneInto(void *) const = 0; + + virtual size_t sizeOf() const = 0; +}; + +/** + * @brief Represents a combination of 'properties', 'patternProperties' and + * 'additionalProperties' constraints + */ +class PropertiesConstraint: public BasicConstraint +{ +public: + PropertiesConstraint() + : m_properties(std::less(), m_allocator), + m_patternProperties(std::less(), m_allocator), + m_additionalProperties(nullptr) { } + + PropertiesConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_properties(std::less(), m_allocator), + m_patternProperties(std::less(), m_allocator), + m_additionalProperties(nullptr) { } + + bool addPatternPropertySubschema(const char *patternProperty, const Subschema *subschema) + { + return m_patternProperties.insert(PropertySchemaMap::value_type( + String(patternProperty, m_allocator), subschema)).second; + } + + template + bool addPatternPropertySubschema(const std::basic_string, AllocatorType> &patternProperty, + const Subschema *subschema) + { + return addPatternPropertySubschema(patternProperty.c_str(), subschema); + } + + bool addPropertySubschema(const char *propertyName, + const Subschema *subschema) + { + return m_properties.insert(PropertySchemaMap::value_type( + String(propertyName, m_allocator), subschema)).second; + } + + template + bool addPropertySubschema(const std::basic_string, AllocatorType> &propertyName, + const Subschema *subschema) + { + return addPropertySubschema(propertyName.c_str(), subschema); + } + + template + void applyToPatternProperties(const FunctorType &fn) const + { + typedef typename PropertySchemaMap::value_type ValueType; + for (const ValueType &value : m_patternProperties) { + if (!fn(value.first, value.second)) { + return; + } + } + } + + template + void applyToProperties(const FunctorType &fn) const + { + typedef typename PropertySchemaMap::value_type ValueType; + for (const ValueType &value : m_properties) { + if (!fn(value.first, value.second)) { + return; + } + } + } + + const Subschema * getAdditionalPropertiesSubschema() const + { + return m_additionalProperties; + } + + void setAdditionalPropertiesSubschema(const Subschema *subschema) + { + m_additionalProperties = subschema; + } + +private: + typedef std::map< + String, + const Subschema *, + std::less, + internal::CustomAllocator> + > PropertySchemaMap; + + PropertySchemaMap m_properties; + PropertySchemaMap m_patternProperties; + + const Subschema *m_additionalProperties; +}; + +class PropertyNamesConstraint: public BasicConstraint +{ +public: + PropertyNamesConstraint() + : m_subschema(nullptr) { } + + PropertyNamesConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_subschema(nullptr) { } + + const Subschema * getSubschema() const + { + return m_subschema; + } + + void setSubschema(const Subschema *subschema) + { + m_subschema = subschema; + } + +private: + const Subschema *m_subschema; +}; + +/** + * @brief Represents a 'required' constraint + */ +class RequiredConstraint: public BasicConstraint +{ +public: + RequiredConstraint() + : m_requiredProperties(std::less(), m_allocator) { } + + RequiredConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_requiredProperties(std::less(), m_allocator) { } + + bool addRequiredProperty(const char *propertyName) + { + return m_requiredProperties.insert(String(propertyName, + Allocator::rebind::other(m_allocator))).second; + } + + template + bool addRequiredProperty(const std::basic_string, AllocatorType> &propertyName) + { + return addRequiredProperty(propertyName.c_str()); + } + + template + void applyToRequiredProperties(const FunctorType &fn) const + { + for (const String &propertyName : m_requiredProperties) { + if (!fn(propertyName)) { + return; + } + } + } + +private: + typedef std::set, + internal::CustomAllocator> RequiredProperties; + + RequiredProperties m_requiredProperties; +}; + +/** + * @brief Represents an 'items' constraint that specifies one sub-schema + * + * A value is considered valid against this constraint if it is an array, and + * each item in the array validates against the sub-schema specified by this + * constraint. + * + * The prefix 'Singular' comes from the fact that array items must validate + * against exactly one sub-schema. + */ +class SingularItemsConstraint: public BasicConstraint +{ +public: + SingularItemsConstraint() + : m_itemsSubschema(nullptr) { } + + SingularItemsConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_itemsSubschema(nullptr) { } + + const Subschema * getItemsSubschema() const + { + return m_itemsSubschema; + } + + void setItemsSubschema(const Subschema *subschema) + { + m_itemsSubschema = subschema; + } + +private: + const Subschema *m_itemsSubschema; +}; + +/** + * @brief Represents a 'type' constraint. + */ +class TypeConstraint: public BasicConstraint +{ +public: + enum JsonType { + kAny, + kArray, + kBoolean, + kInteger, + kNull, + kNumber, + kObject, + kString + }; + + TypeConstraint() + : m_namedTypes(std::less(), m_allocator), + m_schemaTypes(Allocator::rebind::other(m_allocator)) { } + + TypeConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + m_namedTypes(std::less(), m_allocator), + m_schemaTypes(Allocator::rebind::other(m_allocator)) { } + + void addNamedType(JsonType type) + { + m_namedTypes.insert(type); + } + + void addSchemaType(const Subschema *subschema) + { + m_schemaTypes.push_back(subschema); + } + + template + void applyToNamedTypes(const FunctorType &fn) const + { + for (const JsonType namedType : m_namedTypes) { + if (!fn(namedType)) { + return; + } + } + } + + template + void applyToSchemaTypes(const FunctorType &fn) const + { + unsigned int index = 0; + for (const Subschema *subschema : m_schemaTypes) { + if (!fn(index, subschema)) { + return; + } + + index++; + } + } + + template + static JsonType jsonTypeFromString(const std::basic_string, AllocatorType> &typeName) + { + if (typeName.compare("any") == 0) { + return kAny; + } else if (typeName.compare("array") == 0) { + return kArray; + } else if (typeName.compare("boolean") == 0) { + return kBoolean; + } else if (typeName.compare("integer") == 0) { + return kInteger; + } else if (typeName.compare("null") == 0) { + return kNull; + } else if (typeName.compare("number") == 0) { + return kNumber; + } else if (typeName.compare("object") == 0) { + return kObject; + } else if (typeName.compare("string") == 0) { + return kString; + } + + throwRuntimeError("Unrecognised JSON type name '" + + std::string(typeName.c_str()) + "'"); + abort(); + } + +private: + typedef std::set, internal::CustomAllocator> NamedTypes; + + typedef std::vector::other> SchemaTypes; + + /// Set of named JSON types that serve as valid types + NamedTypes m_namedTypes; + + /// Set of sub-schemas that serve as valid types + SchemaTypes m_schemaTypes; +}; + +/** + * @brief Represents a 'uniqueItems' constraint + */ +class UniqueItemsConstraint: public BasicConstraint +{ +public: + UniqueItemsConstraint() = default; + + UniqueItemsConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn) { } +}; + +} // namespace constraints +} // namespace valijson + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/subprojects/valijson/include/valijson/constraints/constraint.hpp b/subprojects/valijson/include/valijson/constraints/constraint.hpp new file mode 100644 index 000000000..03b6444cd --- /dev/null +++ b/subprojects/valijson/include/valijson/constraints/constraint.hpp @@ -0,0 +1,50 @@ +#pragma once + +namespace valijson { +namespace constraints { + +class ConstraintVisitor; + +/** + * @brief Interface that must be implemented by concrete constraint types. + * + * @todo Consider using something like the boost::cloneable concept here. + */ +struct Constraint +{ + /// Typedef for custom new-/malloc-like function + typedef void * (*CustomAlloc)(size_t size); + + /// Typedef for custom free-like function + typedef void (*CustomFree)(void *); + + /** + * @brief Virtual destructor. + */ + virtual ~Constraint() = default; + + /** + * @brief Perform an action on the constraint using the visitor pattern. + * + * Note that Constraints cannot be modified by visitors. + * + * @param visitor Reference to a ConstraintVisitor object. + * + * @returns the boolean value returned by one of the visitor's visit + * functions. + */ + virtual bool accept(ConstraintVisitor &visitor) const = 0; + + /** + * @brief Make a copy of a constraint. + * + * Note that this should be a deep copy of the constraint. + * + * @returns an owning-pointer to the new constraint. + */ + virtual Constraint * clone(CustomAlloc, CustomFree) const = 0; + +}; + +} // namespace constraints +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/constraints/constraint_visitor.hpp b/subprojects/valijson/include/valijson/constraints/constraint_visitor.hpp new file mode 100644 index 000000000..da3f3d7cf --- /dev/null +++ b/subprojects/valijson/include/valijson/constraints/constraint_visitor.hpp @@ -0,0 +1,104 @@ +#pragma once + +namespace valijson { +namespace constraints { + +class AllOfConstraint; +class AnyOfConstraint; +class ConditionalConstraint; +class ConstConstraint; +class ContainsConstraint; +class DependenciesConstraint; +class EnumConstraint; +class LinearItemsConstraint; +class MaxItemsConstraint; +class MaximumConstraint; +class MaxLengthConstraint; +class MaxPropertiesConstraint; +class MinItemsConstraint; +class MinimumConstraint; +class MinLengthConstraint; +class MinPropertiesConstraint; +class MultipleOfDoubleConstraint; +class MultipleOfIntConstraint; +class NotConstraint; +class OneOfConstraint; +class PatternConstraint; +class PolyConstraint; +class PropertiesConstraint; +class PropertyNamesConstraint; +class RequiredConstraint; +class SingularItemsConstraint; +class TypeConstraint; +class UniqueItemsConstraint; + +/// Interface to allow usage of the visitor pattern with Constraints +class ConstraintVisitor +{ +protected: + virtual ~ConstraintVisitor() = default; + + // Shorten type names for derived classes outside of this namespace + typedef constraints::AllOfConstraint AllOfConstraint; + typedef constraints::AnyOfConstraint AnyOfConstraint; + typedef constraints::ConditionalConstraint ConditionalConstraint; + typedef constraints::ConstConstraint ConstConstraint; + typedef constraints::ContainsConstraint ContainsConstraint; + typedef constraints::DependenciesConstraint DependenciesConstraint; + typedef constraints::EnumConstraint EnumConstraint; + typedef constraints::LinearItemsConstraint LinearItemsConstraint; + typedef constraints::MaximumConstraint MaximumConstraint; + typedef constraints::MaxItemsConstraint MaxItemsConstraint; + typedef constraints::MaxLengthConstraint MaxLengthConstraint; + typedef constraints::MaxPropertiesConstraint MaxPropertiesConstraint; + typedef constraints::MinimumConstraint MinimumConstraint; + typedef constraints::MinItemsConstraint MinItemsConstraint; + typedef constraints::MinLengthConstraint MinLengthConstraint; + typedef constraints::MinPropertiesConstraint MinPropertiesConstraint; + typedef constraints::MultipleOfDoubleConstraint MultipleOfDoubleConstraint; + typedef constraints::MultipleOfIntConstraint MultipleOfIntConstraint; + typedef constraints::NotConstraint NotConstraint; + typedef constraints::OneOfConstraint OneOfConstraint; + typedef constraints::PatternConstraint PatternConstraint; + typedef constraints::PolyConstraint PolyConstraint; + typedef constraints::PropertiesConstraint PropertiesConstraint; + typedef constraints::PropertyNamesConstraint PropertyNamesConstraint; + typedef constraints::RequiredConstraint RequiredConstraint; + typedef constraints::SingularItemsConstraint SingularItemsConstraint; + typedef constraints::TypeConstraint TypeConstraint; + typedef constraints::UniqueItemsConstraint UniqueItemsConstraint; + +public: + + virtual bool visit(const AllOfConstraint &) = 0; + virtual bool visit(const AnyOfConstraint &) = 0; + virtual bool visit(const ConditionalConstraint &) = 0; + virtual bool visit(const ConstConstraint &) = 0; + virtual bool visit(const ContainsConstraint &) = 0; + virtual bool visit(const DependenciesConstraint &) = 0; + virtual bool visit(const EnumConstraint &) = 0; + virtual bool visit(const LinearItemsConstraint &) = 0; + virtual bool visit(const MaximumConstraint &) = 0; + virtual bool visit(const MaxItemsConstraint &) = 0; + virtual bool visit(const MaxLengthConstraint &) = 0; + virtual bool visit(const MaxPropertiesConstraint &) = 0; + virtual bool visit(const MinimumConstraint &) = 0; + virtual bool visit(const MinItemsConstraint &) = 0; + virtual bool visit(const MinLengthConstraint &) = 0; + virtual bool visit(const MinPropertiesConstraint &) = 0; + virtual bool visit(const MultipleOfDoubleConstraint &) = 0; + virtual bool visit(const MultipleOfIntConstraint &) = 0; + virtual bool visit(const NotConstraint &) = 0; + virtual bool visit(const OneOfConstraint &) = 0; + virtual bool visit(const PatternConstraint &) = 0; + virtual bool visit(const PolyConstraint &) = 0; + virtual bool visit(const PropertiesConstraint &) = 0; + virtual bool visit(const PropertyNamesConstraint &) = 0; + virtual bool visit(const RequiredConstraint &) = 0; + virtual bool visit(const SingularItemsConstraint &) = 0; + virtual bool visit(const TypeConstraint &) = 0; + virtual bool visit(const UniqueItemsConstraint &) = 0; +}; + +} // namespace constraints +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/exceptions.hpp b/subprojects/valijson/include/valijson/exceptions.hpp new file mode 100644 index 000000000..a4188259e --- /dev/null +++ b/subprojects/valijson/include/valijson/exceptions.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace valijson { +#if defined(_MSC_VER) && _MSC_VER == 1800 +#define VALIJSON_NORETURN __declspec(noreturn) +#else +#define VALIJSON_NORETURN [[noreturn]] +#endif + +#if VALIJSON_USE_EXCEPTIONS +#include + +VALIJSON_NORETURN inline void throwRuntimeError(const std::string& msg) { + throw std::runtime_error(msg); +} + +VALIJSON_NORETURN inline void throwLogicError(const std::string& msg) { + throw std::logic_error(msg); +} +#else +VALIJSON_NORETURN inline void throwRuntimeError(const std::string& msg) { + std::cerr << msg << std::endl; + abort(); +} +VALIJSON_NORETURN inline void throwLogicError(const std::string& msg) { + std::cerr << msg << std::endl; + abort(); +} + +#endif + +VALIJSON_NORETURN inline void throwNotSupported() { + throwRuntimeError("Not supported"); \ +} + +} // namespace valijson + + diff --git a/subprojects/valijson/include/valijson/internal/custom_allocator.hpp b/subprojects/valijson/include/valijson/internal/custom_allocator.hpp new file mode 100644 index 000000000..07e1d6117 --- /dev/null +++ b/subprojects/valijson/include/valijson/internal/custom_allocator.hpp @@ -0,0 +1,107 @@ +#pragma once + +namespace valijson { +namespace internal { + +template +class CustomAllocator +{ +public: + /// Typedef for custom new-/malloc-like function + typedef void * (*CustomAlloc)(size_t size); + + /// Typedef for custom free-like function + typedef void (*CustomFree)(void *); + + // Standard allocator typedefs + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + template + struct rebind + { + typedef CustomAllocator other; + }; + + CustomAllocator() + : m_allocFn(::operator new), + m_freeFn(::operator delete) { } + + CustomAllocator(CustomAlloc allocFn, CustomFree freeFn) + : m_allocFn(allocFn), + m_freeFn(freeFn) { } + + CustomAllocator(const CustomAllocator &other) + : m_allocFn(other.m_allocFn), + m_freeFn(other.m_freeFn) { } + + template + CustomAllocator(CustomAllocator const &other) + : m_allocFn(other.m_allocFn), + m_freeFn(other.m_freeFn) { } + + CustomAllocator & operator=(const CustomAllocator &other) + { + m_allocFn = other.m_allocFn; + m_freeFn = other.m_freeFn; + + return *this; + } + + pointer address(reference r) + { + return &r; + } + + const_pointer address(const_reference r) + { + return &r; + } + + pointer allocate(size_type cnt, const void * = nullptr) + { + return reinterpret_cast(m_allocFn(cnt * sizeof(T))); + } + + void deallocate(pointer p, size_type) + { + m_freeFn(p); + } + + size_type max_size() const + { + return std::numeric_limits::max() / sizeof(T); + } + + void construct(pointer p, const T& t) + { + new(p) T(t); + } + + void destroy(pointer p) + { + p->~T(); + } + + bool operator==(const CustomAllocator &other) const + { + return other.m_allocFn == m_allocFn && other.m_freeFn == m_freeFn; + } + + bool operator!=(const CustomAllocator &other) const + { + return !operator==(other); + } + + CustomAlloc m_allocFn; + + CustomFree m_freeFn; +}; + +} // end namespace internal +} // end namespace valijson diff --git a/subprojects/valijson/include/valijson/internal/debug.hpp b/subprojects/valijson/include/valijson/internal/debug.hpp new file mode 100644 index 000000000..25c3dd770 --- /dev/null +++ b/subprojects/valijson/include/valijson/internal/debug.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace valijson { +namespace internal { + +template +std::string nodeTypeAsString(const AdapterType &node) { + if (node.isArray()) { + return "array"; + } else if (node.isObject()) { + return "object"; + } else if (node.isString()) { + return "string"; + } else if (node.isNull()) { + return "null"; + } else if (node.isInteger()) { + return "integer"; + } else if (node.isDouble()) { + return "double"; + } else if (node.isBool()) { + return "bool"; + } + + return "unknown"; +} + +} // end namespace internal +} // end namespace valijson diff --git a/subprojects/valijson/include/valijson/internal/json_pointer.hpp b/subprojects/valijson/include/valijson/internal/json_pointer.hpp new file mode 100644 index 000000000..b290c02a2 --- /dev/null +++ b/subprojects/valijson/include/valijson/internal/json_pointer.hpp @@ -0,0 +1,275 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4702 ) +#endif + +namespace valijson { +namespace internal { +namespace json_pointer { + +/** + * @brief Replace all occurrences of `search` with `replace`. Modifies `subject` in place. + * + * @param subject string to operate on + * @param search string to search + * @param replace replacement string + */ +inline void replaceAllInPlace(std::string& subject, const char* search, + const char* replace) +{ + size_t pos = 0; + + while((pos = subject.find(search, pos)) != std::string::npos) { + subject.replace(pos, strlen(search), replace); + pos += strlen(replace); + } +} + +/** + * @brief Return the char value corresponding to a 2-digit hexadecimal string + * + * @throws std::runtime_error for strings that are not exactly two characters + * in length and for strings that contain non-hexadecimal characters + * + * @return decoded char value corresponding to the hexadecimal string + */ +inline char decodePercentEncodedChar(const std::string &digits) +{ + if (digits.length() != 2) { + throwRuntimeError("Failed to decode %-encoded character '" + + digits + "' due to unexpected number of characters; " + "expected two characters"); + } + + errno = 0; + const char *begin = digits.c_str(); + char *end = nullptr; + const unsigned long value = strtoul(begin, &end, 16); + if (end != begin && *end != '\0') { + throwRuntimeError("Failed to decode %-encoded character '" + + digits + "'"); + } + + return char(value); +} + +/** + * @brief Extract and transform the token between two iterators + * + * This function is responsible for extracting a JSON Reference token from + * between two iterators, and performing any necessary transformations, before + * returning the resulting string. Its main purpose is to replace the escaped + * character sequences defined in the RFC-6901 (JSON Pointer), and to decode + * %-encoded character sequences defined in RFC-3986 (URI). + * + * The encoding used in RFC-3986 should be familiar to many developers, but + * the escaped character sequences used in JSON Pointers may be less so. From + * the JSON Pointer specification (RFC 6901, April 2013): + * + * Evaluation of each reference token begins by decoding any escaped + * character sequence. This is performed by first transforming any + * occurrence of the sequence '~1' to '/', and then transforming any + * occurrence of the sequence '~0' to '~'. By performing the + * substitutions in this order, an implementation avoids the error of + * turning '~01' first into '~1' and then into '/', which would be + * incorrect (the string '~01' correctly becomes '~1' after + * transformation). + * + * @param begin iterator pointing to beginning of a token + * @param end iterator pointing to one character past the end of the token + * + * @return string with escaped character sequences replaced + * + */ +inline std::string extractReferenceToken(std::string::const_iterator begin, + std::string::const_iterator end) +{ + std::string token(begin, end); + + // Replace JSON Pointer-specific escaped character sequences + replaceAllInPlace(token, "~1", "/"); + replaceAllInPlace(token, "~0", "~"); + + // Replace %-encoded character sequences with their actual characters + for (size_t n = token.find('%'); n != std::string::npos; + n = token.find('%', n + 1)) { + +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + const char c = decodePercentEncodedChar(token.substr(n + 1, 2)); + token.replace(n, 3, &c, 1); +#if VALIJSON_USE_EXCEPTIONS + } catch (const std::runtime_error &e) { + throwRuntimeError( + std::string(e.what()) + "; in token: " + token); + } +#endif + } + + return token; +} + +/** + * @brief Recursively locate the value referenced by a JSON Pointer + * + * This function takes both a string reference and an iterator to the beginning + * of the substring that is being resolved. This iterator is expected to point + * to the beginning of a reference token, whose length will be determined by + * searching for the next delimiter ('/' or '\0'). A reference token must be + * at least one character in length to be considered valid. + * + * Once the next reference token has been identified, it will be used either as + * an array index or as an the name an object member. The validity of a + * reference token depends on the type of the node currently being traversed, + * and the applicability of the token to that node. For example, an array can + * only be dereferenced by a non-negative integral index. + * + * Once the next node has been identified, the length of the remaining portion + * of the JSON Pointer will be used to determine whether recursion should + * terminate. + * + * @param node current node in recursive evaluation of JSON Pointer + * @param jsonPointer string containing complete JSON Pointer + * @param jsonPointerItr string iterator pointing the beginning of the next + * reference token + * + * @return an instance of AdapterType that wraps the dereferenced node + */ +template +inline AdapterType resolveJsonPointer( + const AdapterType &node, + const std::string &jsonPointer, + const std::string::const_iterator jsonPointerItr) +{ + // TODO: This function will probably need to implement support for + // fetching documents referenced by JSON Pointers, similar to the + // populateSchema function. + + const std::string::const_iterator jsonPointerEnd = jsonPointer.end(); + + // Terminate recursion if all reference tokens have been consumed + if (jsonPointerItr == jsonPointerEnd) { + return node; + } + + // Reference tokens must begin with a leading slash + if (*jsonPointerItr != '/') { + throwRuntimeError("Expected reference token to begin with " + "leading slash; remaining tokens: " + + std::string(jsonPointerItr, jsonPointerEnd)); + } + + // Find iterator that points to next slash or newline character; this is + // one character past the end of the current reference token + std::string::const_iterator jsonPointerNext = + std::find(jsonPointerItr + 1, jsonPointerEnd, '/'); + + // Extract the next reference token + const std::string referenceToken = extractReferenceToken( + jsonPointerItr + 1, jsonPointerNext); + + // Empty reference tokens should be ignored + if (referenceToken.empty()) { + return resolveJsonPointer(node, jsonPointer, jsonPointerNext); + + } else if (node.isArray()) { + if (referenceToken == "-") { + throwRuntimeError("Hyphens cannot be used as array indices " + "since the requested array element does not yet exist"); + } + +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + // Fragment must be non-negative integer + const uint64_t index = std::stoul(referenceToken); + typedef typename AdapterType::Array Array; + const Array arr = node.asArray(); + typename Array::const_iterator itr = arr.begin(); + const uint64_t arrSize = arr.size(); + + if (arrSize == 0 || index > arrSize - 1) { + throwRuntimeError("Expected reference token to identify " + "an element in the current array, but array index is " + "out of bounds; actual token: " + referenceToken); + } + + if (index > static_cast(std::numeric_limits::max())) { + throwRuntimeError("Array index out of bounds; hard " + "limit is " + std::to_string( + std::numeric_limits::max())); + } + + itr.advance(static_cast(index)); + + // Recursively process the remaining tokens + return resolveJsonPointer(*itr, jsonPointer, jsonPointerNext); + +#if VALIJSON_USE_EXCEPTIONS + } catch (std::invalid_argument &) { + throwRuntimeError("Expected reference token to contain a " + "non-negative integer to identify an element in the " + "current array; actual token: " + referenceToken); + } +#endif + } else if (node.maybeObject()) { + // Fragment must identify a member of the candidate object + typedef typename AdapterType::Object Object; + + const Object object = node.asObject(); + typename Object::const_iterator itr = object.find( + referenceToken); + if (itr == object.end()) { + throwRuntimeError("Expected reference token to identify an " + "element in the current object; " + "actual token: " + referenceToken); + abort(); + } + + // Recursively process the remaining tokens + return resolveJsonPointer(itr->second, jsonPointer, jsonPointerNext); + } + + throwRuntimeError("Expected end of JSON Pointer, but at least " + "one reference token has not been processed; remaining tokens: " + + std::string(jsonPointerNext, jsonPointerEnd)); + abort(); +} + +/** + * @brief Return the JSON Value referenced by a JSON Pointer + * + * @param rootNode node to use as root for JSON Pointer resolution + * @param jsonPointer string containing JSON Pointer + * + * @return an instance AdapterType in the specified document + */ +template +inline AdapterType resolveJsonPointer( + const AdapterType &rootNode, + const std::string &jsonPointer) +{ + return resolveJsonPointer(rootNode, jsonPointer, jsonPointer.begin()); +} + +} // namespace json_pointer +} // namespace internal +} // namespace valijson + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/subprojects/valijson/include/valijson/internal/json_reference.hpp b/subprojects/valijson/include/valijson/internal/json_reference.hpp new file mode 100644 index 000000000..fc67a3477 --- /dev/null +++ b/subprojects/valijson/include/valijson/internal/json_reference.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include + +namespace valijson { +namespace internal { +namespace json_reference { + +/** + * @brief Extract URI from JSON Reference relative to the current schema + * + * @param jsonRef JSON Reference to extract from + * @param schema Schema that JSON Reference URI is relative to + * + * @return Optional string containing URI + */ +inline opt::optional getJsonReferenceUri( + const std::string &jsonRef) +{ + const size_t ptrPos = jsonRef.find('#'); + if (ptrPos == 0) { + // The JSON Reference does not contain a URI, but might contain a + // JSON Pointer that refers to the current document + return opt::optional(); + } else if (ptrPos != std::string::npos) { + // The JSON Reference contains a URI and possibly a JSON Pointer + return jsonRef.substr(0, ptrPos); + } + + // The entire JSON Reference should be treated as a URI + return jsonRef; +} + +/** + * @brief Extract JSON Pointer portion of a JSON Reference + * + * @param jsonRef JSON Reference to extract from + * + * @return Optional string containing JSON Pointer + */ +inline opt::optional getJsonReferencePointer( + const std::string &jsonRef) +{ + // Attempt to extract JSON Pointer if '#' character is present. Note + // that a valid pointer would contain at least a leading forward + // slash character. + const size_t ptrPos = jsonRef.find('#'); + if (ptrPos != std::string::npos) { + return jsonRef.substr(ptrPos + 1); + } + + return opt::optional(); +} + +} // namespace json_reference +} // namespace internal +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/internal/optional.hpp b/subprojects/valijson/include/valijson/internal/optional.hpp new file mode 100644 index 000000000..c9bc47b61 --- /dev/null +++ b/subprojects/valijson/include/valijson/internal/optional.hpp @@ -0,0 +1,14 @@ +#pragma once + +#if __cplusplus >= 201703 +// Visual C++ only supports __has_include in versions 14.12 and greater +# if !defined(_MSC_VER) || _MSC_VER >= 1912 +# if __has_include() +# include +namespace opt = std; +# endif +# endif +#else +# include +namespace opt = std::experimental; +#endif diff --git a/subprojects/valijson/include/valijson/internal/uri.hpp b/subprojects/valijson/include/valijson/internal/uri.hpp new file mode 100644 index 000000000..d04a18b18 --- /dev/null +++ b/subprojects/valijson/include/valijson/internal/uri.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace valijson { +namespace internal { +namespace uri { + +/** + * @brief Placeholder function to check whether a URI is absolute + * + * This function just checks for '://' + */ +inline bool isUriAbsolute(const std::string &documentUri) +{ + static const char * placeholderMarker = "://"; + + return documentUri.find(placeholderMarker) != std::string::npos; +} + +/** + * Placeholder function to resolve a relative URI within a given scope + */ +inline std::string resolveRelativeUri( + const std::string &resolutionScope, + const std::string &relativeUri) +{ + return resolutionScope + relativeUri; +} + +} // namespace uri +} // namespace internal +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/schema.hpp b/subprojects/valijson/include/valijson/schema.hpp new file mode 100644 index 000000000..a3c3f24e4 --- /dev/null +++ b/subprojects/valijson/include/valijson/schema.hpp @@ -0,0 +1,224 @@ +#pragma once + +#include +#include + +#include +#include + +namespace valijson { + +/** + * Represents the root of a JSON Schema + * + * The root is distinct from other sub-schemas because it is the canonical + * starting point for validation of a document against a given a JSON Schema. + */ +class Schema: public Subschema +{ +public: + /** + * @brief Construct a new Schema instance with no constraints + */ + Schema() + : sharedEmptySubschema(newSubschema()) { } + + /** + * @brief Construct a new Schema using custom memory management + * functions + * + * @param allocFn malloc- or new-like function to allocate memory + * within Schema, such as for Subschema instances + * @param freeFn free-like function to free memory allocated with + * the `customAlloc` function + */ + Schema(CustomAlloc allocFn, CustomFree freeFn) + : Subschema(allocFn, freeFn), + sharedEmptySubschema(newSubschema()) { } + + // Disable copy construction + Schema(const Schema &) = delete; + + // Disable copy assignment + Schema & operator=(const Schema &) = delete; + + /** + * @brief Clean up and free all memory managed by the Schema + * + * Note that any Subschema pointers created and returned by this Schema + * should be considered invalid. + */ + ~Schema() override + { + sharedEmptySubschema->~Subschema(); + m_freeFn(const_cast(sharedEmptySubschema)); + sharedEmptySubschema = nullptr; + +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + for (auto subschema : subschemaSet) { + subschema->~Subschema(); + m_freeFn(subschema); + } +#if VALIJSON_USE_EXCEPTIONS + } catch (const std::exception &e) { + fprintf(stderr, "Caught an exception while destroying Schema: %s", + e.what()); + } +#endif + } + + /** + * @brief Copy a constraint to a specific sub-schema + * + * @param constraint reference to a constraint that will be copied into + * the sub-schema + * @param subschema pointer to the sub-schema that will own the copied + * constraint + * + * @throws std::runtime_error if the sub-schema is not owned by this Schema + * instance + */ + void addConstraintToSubschema(const Constraint &constraint, + const Subschema *subschema) + { + // TODO: Check heirarchy for subschemas that do not belong... + + mutableSubschema(subschema)->addConstraint(constraint); + } + + /** + * @brief Create a new Subschema instance that is owned by this Schema + * + * @returns const pointer to the new Subschema instance + */ + const Subschema * createSubschema() + { + Subschema *subschema = newSubschema(); + +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + if (!subschemaSet.insert(subschema).second) { + throwRuntimeError( + "Failed to store pointer for new sub-schema"); + } +#if VALIJSON_USE_EXCEPTIONS + } catch (...) { + subschema->~Subschema(); + m_freeFn(subschema); + throw; + } +#endif + return subschema; + } + + /** + * @brief Return a pointer to the shared empty schema + */ + const Subschema * emptySubschema() const + { + return sharedEmptySubschema; + } + + /** + * @brief Get a pointer to the root sub-schema of this Schema instance + */ + const Subschema * root() const + { + return this; + } + + void setAlwaysInvalid(const Subschema *subschema, bool value) + { + mutableSubschema(subschema)->setAlwaysInvalid(value); + } + + /** + * @brief Update the description for one of the sub-schemas owned by this + * Schema instance + * + * @param subschema sub-schema to update + * @param description new description + */ + void setSubschemaDescription(const Subschema *subschema, + const std::string &description) + { + mutableSubschema(subschema)->setDescription(description); + } + + /** + * @brief Update the ID for one of the sub-schemas owned by this Schema + * instance + * + * @param subschema sub-schema to update + * @param id new ID + */ + void setSubschemaId(const Subschema *subschema, const std::string &id) + { + mutableSubschema(subschema)->setId(id); + } + + /** + * @brief Update the title for one of the sub-schemas owned by this Schema + * instance + * + * @param subschema sub-schema to update + * @param title new title + */ + void setSubschemaTitle(const Subschema *subschema, const std::string &title) + { + mutableSubschema(subschema)->setTitle(title); + } + +private: + + Subschema *newSubschema() + { + void *ptr = m_allocFn(sizeof(Subschema)); + if (!ptr) { + throwRuntimeError( + "Failed to allocate memory for shared empty sub-schema"); + } + +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + return new (ptr) Subschema(); +#if VALIJSON_USE_EXCEPTIONS + } catch (...) { + m_freeFn(ptr); + throw; + } +#endif + } + + Subschema * mutableSubschema(const Subschema *subschema) + { + if (subschema == this) { + return this; + } + + if (subschema == sharedEmptySubschema) { + throwRuntimeError( + "Cannot modify the shared empty sub-schema"); + } + + auto *noConst = const_cast(subschema); + if (subschemaSet.find(noConst) == subschemaSet.end()) { + throwRuntimeError( + "Subschema pointer is not owned by this Schema instance"); + } + + return noConst; + } + + /// Set of Subschema instances owned by this schema + std::set subschemaSet; + + /// Empty schema that can be reused by multiple constraints + const Subschema *sharedEmptySubschema; +}; + +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/schema_parser.hpp b/subprojects/valijson/include/valijson/schema_parser.hpp new file mode 100644 index 000000000..908cd182e --- /dev/null +++ b/subprojects/valijson/include/valijson/schema_parser.hpp @@ -0,0 +1,2282 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace valijson { + +/** + * @brief Parser for populating a Schema based on a JSON Schema document. + * + * The SchemaParser class supports Drafts 3 and 4 of JSON Schema, however + * Draft 3 support should be considered deprecated. + * + * The functions provided by this class have been templated so that they can + * be used with different Adapter types. + */ +class SchemaParser +{ +public: + /// Supported versions of JSON Schema + enum Version { + kDraft3, ///< @deprecated JSON Schema v3 has been superseded by v4 + kDraft4, + kDraft7 + }; + + /** + * @brief Construct a new SchemaParser for a given version of JSON Schema + * + * @param version Version of JSON Schema that will be expected + */ + explicit SchemaParser(const Version version = kDraft7) + : m_version(version) { } + + /** + * @brief Release memory associated with custom ConstraintBuilders + */ + virtual ~SchemaParser() + { + for (const auto& entry : constraintBuilders) { + delete entry.second; + } + } + + /** + * @brief Struct to contain templated function type for fetching documents + */ + template + struct FunctionPtrs + { + typedef typename adapters::AdapterTraits::DocumentType DocumentType; + + /// Templated function pointer type for fetching remote documents + typedef std::function FetchDoc; + + /// Templated function pointer type for freeing fetched documents + typedef std::function FreeDoc; + }; + + /** + * @brief Add a custom contraint to this SchemaParser + + * @param key name that will be used to identify relevant constraints + * while parsing a schema document + * @param builder pointer to a subclass of ConstraintBuilder that can + * parse custom constraints found in a schema document, + * and return an appropriate instance of Constraint; this + * class guarantees that it will take ownership of this + * pointer - unless this function throws an exception + * + * @todo consider accepting a list of custom ConstraintBuilders in + * constructor, so that this class remains immutable after + * construction + * + * @todo Add additional checks for key conflicts, empty keys, and + * potential restrictions relating to case sensitivity + */ + void addConstraintBuilder(const std::string &key, const ConstraintBuilder *builder) + { + constraintBuilders.push_back(std::make_pair(key, builder)); + } + + /** + * @brief Populate a Schema object from JSON Schema document + * + * When processing Draft 3 schemas, the parentSubschema and ownName pointers + * should be set in contexts where a 'required' constraint would be valid. + * These are used to add a RequiredConstraint object to the Schema that + * contains the required property. + * + * @param node Reference to node to parse + * @param schema Reference to Schema to populate + * @param fetchDoc Function to fetch remote JSON documents (optional) + */ + template + void populateSchema( + const AdapterType &node, + Schema &schema, + typename FunctionPtrs::FetchDoc fetchDoc = nullptr , + typename FunctionPtrs::FreeDoc freeDoc = nullptr ) + { + if ((fetchDoc == nullptr ) ^ (freeDoc == nullptr)) { + throwRuntimeError("Remote document fetching can't be enabled without both fetch and free functions"); + } + + typename DocumentCache::Type docCache; + SchemaCache schemaCache; +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + resolveThenPopulateSchema(schema, node, node, schema, opt::optional(), "", fetchDoc, nullptr, + nullptr, docCache, schemaCache); +#if VALIJSON_USE_EXCEPTIONS + } catch (...) { + freeDocumentCache(docCache, freeDoc); + throw; + } +#endif + + freeDocumentCache(docCache, freeDoc); + } + +private: + + typedef std::vector> + ConstraintBuilders; + + ConstraintBuilders constraintBuilders; + + template + struct DocumentCache + { + typedef typename adapters::AdapterTraits::DocumentType DocumentType; + + typedef std::map Type; + }; + + typedef std::map SchemaCache; + + /** + * @brief Free memory used by fetched documents + * + * If a custom 'free' function has not been provided, then the default + * delete operator will be used. + * + * @param docCache collection of fetched documents to free + * @param freeDoc optional custom free function + */ + template + void freeDocumentCache(const typename DocumentCache::Type + &docCache, typename FunctionPtrs::FreeDoc freeDoc) + { + typedef typename DocumentCache::Type DocCacheType; + + for (const typename DocCacheType::value_type &v : docCache) { + freeDoc(v.second); + } + } + + /** + * @brief Find the absolute URI for a document, within a resolution scope + * + * This function captures five different cases that can occur when + * attempting to resolve a document URI within a particular resolution + * scope: + * + * - resolution scope not present, but absolute document URI is + * => document URI as-is + * - resolution scope not present, and document URI is relative or absent + * => no result + * - resolution scope is present, and document URI is a relative path + * => resolve document URI relative to resolution scope + * - resolution scope is present, and document URI is absolute + * => document URI as-is + * - resolution scope is present, but document URI is not + * => resolution scope as-is + * + * This function assumes that the resolution scope is absolute. + * + * When resolving a document URI relative to the resolution scope, the + * document URI should be used to replace the path, query and fragment + * portions of URI provided by the resolution scope. + */ + virtual opt::optional findAbsoluteDocumentUri( + const opt::optional& resolutionScope, + const opt::optional& documentUri) + { + if (resolutionScope) { + if (documentUri) { + if (internal::uri::isUriAbsolute(*documentUri)) { + return *documentUri; + } else { + return internal::uri::resolveRelativeUri(*resolutionScope, *documentUri); + } + } else { + return *resolutionScope; + } + } else if (documentUri && internal::uri::isUriAbsolute(*documentUri)) { + return *documentUri; + } else { + return opt::optional(); + } + } + + /** + * @brief Extract a JSON Reference string from a node + * + * @param node node to extract the JSON Reference from + * @param result reference to string to set with the result + * + * @throws std::invalid_argument if node is an object containing a `$ref` + * property but with a value that cannot be interpreted as a string + * + * @return \c true if a JSON Reference was extracted; \c false otherwise + */ + template + bool extractJsonReference(const AdapterType &node, std::string &result) + { + if (!node.isObject()) { + return false; + } + + const typename AdapterType::Object o = node.getObject(); + const typename AdapterType::Object::const_iterator itr = o.find("$ref"); + if (itr == o.end()) { + return false; + } else if (!itr->second.getString(result)) { + throwRuntimeError("$ref property expected to contain string value."); + } + + return true; + } + + /** + * Sanitise an optional JSON Pointer, trimming trailing slashes + */ + static std::string sanitiseJsonPointer(const opt::optional& input) + { + if (input) { + // Trim trailing slash(es) + std::string sanitised = *input; + sanitised.erase(sanitised.find_last_not_of('/') + 1, + std::string::npos); + + return sanitised; + } + + // If the JSON Pointer is not set, assume that the URI points to + // the root of the document + return ""; + } + + /** + * @brief Search the schema cache for a schema matching a given key + * + * If the key is not present in the query cache, a nullptr will be + * returned, and the contents of the cache will remain unchanged. This is + * in contrast to the behaviour of the std::map [] operator, which would + * add the nullptr to the cache. + * + * @param schemaCache schema cache to query + * @param queryKey key to search for + * + * @return shared pointer to Schema if found, nullptr otherwise + */ + static const Subschema * querySchemaCache(SchemaCache &schemaCache, + const std::string &queryKey) + { + const SchemaCache::iterator itr = schemaCache.find(queryKey); + if (itr == schemaCache.end()) { + return nullptr; + } + + return itr->second; + } + + /** + * @brief Add entries to the schema cache for a given list of keys + * + * @param schemaCache schema cache to update + * @param keysToCreate list of keys to create entries for + * @param schema shared pointer to schema that keys will map to + * + * @throws std::logic_error if any of the keys are already present in the + * schema cache. This behaviour is intended to help detect incorrect + * usage of the schema cache during development, and is not expected + * to occur otherwise, even for malformed schemas. + */ + static void updateSchemaCache(SchemaCache &schemaCache, + const std::vector &keysToCreate, + const Subschema *schema) + { + for (const std::string &keyToCreate : keysToCreate) { + const SchemaCache::value_type value(keyToCreate, schema); + if (!schemaCache.insert(value).second) { + throwLogicError("Key '" + keyToCreate + "' already in schema cache."); + } + } + } + + /** + * @brief Recursive helper function for retrieving or creating schemas + * + * This function will be applied recursively until a concrete node is found. + * A concrete node is a node that contains actual schema constraints rather + * than a JSON Reference. + * + * This termination condition may be trigged by visiting the concrete node + * at the end of a series of $ref nodes, or by finding a schema for one of + * those $ref nodes in the schema cache. An entry will be added to the + * schema cache for each node visited on the path to the concrete node. + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and + * modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document + * @param node Reference to the node to parse + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param parentSchema Optional pointer to the parent schema, used to + * support required keyword in Draft 3 + * @param ownName Optional pointer to a node name, used to support + * the 'required' keyword in Draft 3 + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + * @param newCacheKeys A list of keys that should be added to the cache + * when recursion terminates + */ + template + const Subschema * makeOrReuseSchema( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + const Subschema *parentSubschema, + const std::string *ownName, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache, + std::vector &newCacheKeys) + { + std::string jsonRef; + + // Check for the first termination condition (found a non-$ref node) + if (!extractJsonReference(node, jsonRef)) { + + // Construct a key that we can use to search the schema cache for + // a schema corresponding to the current node + const std::string schemaCacheKey = currentScope ? (*currentScope + nodePath) : nodePath; + + // Retrieve an existing schema from the cache if possible + const Subschema *cachedPtr = querySchemaCache(schemaCache, schemaCacheKey); + + // Create a new schema otherwise + const Subschema *subschema = cachedPtr ? cachedPtr : rootSchema.createSubschema(); + + // Add cache entries for keys belonging to any $ref nodes that were + // visited before arriving at the current node + updateSchemaCache(schemaCache, newCacheKeys, subschema); + + // Schema cache did not contain a pre-existing schema corresponding + // to the current node, so the schema that was returned will need + // to be populated + if (!cachedPtr) { + populateSchema(rootSchema, rootNode, node, *subschema, + currentScope, nodePath, fetchDoc, parentSubschema, + ownName, docCache, schemaCache); + } + + return subschema; + } + + // Returns a document URI if the reference points somewhere + // other than the current document + const opt::optional documentUri = internal::json_reference::getJsonReferenceUri(jsonRef); + + // Extract JSON Pointer from JSON Reference, with any trailing + // slashes removed so that keys in the schema cache end + // consistently + const std::string actualJsonPointer = sanitiseJsonPointer( + internal::json_reference::getJsonReferencePointer(jsonRef)); + + // Determine the actual document URI based on the resolution + // scope. An absolute document URI will take precedence when + // present, otherwise we need to resolve the URI relative to + // the current resolution scope + const opt::optional actualDocumentUri = findAbsoluteDocumentUri(currentScope, documentUri); + + // Construct a key to search the schema cache for an existing schema + const std::string queryKey = actualDocumentUri ? (*actualDocumentUri + actualJsonPointer) : actualJsonPointer; + + // Check for the second termination condition (found a $ref node that + // already has an entry in the schema cache) + const Subschema *cachedPtr = querySchemaCache(schemaCache, queryKey); + if (cachedPtr) { + updateSchemaCache(schemaCache, newCacheKeys, cachedPtr); + return cachedPtr; + } + + if (actualDocumentUri && (!currentScope || *actualDocumentUri != *currentScope)) { + const typename FunctionPtrs::DocumentType *newDoc = nullptr; + + // Have we seen this document before? + typename DocumentCache::Type::iterator docCacheItr = + docCache.find(*actualDocumentUri); + if (docCacheItr == docCache.end()) { + // Resolve reference against remote document + if (!fetchDoc) { + throwRuntimeError("Fetching of remote JSON References not enabled."); + } + + // Returns a pointer to the remote document that was + // retrieved, or null if retrieval failed. This class + // will take ownership of the pointer, and call freeDoc + // when it is no longer needed. + newDoc = fetchDoc(*actualDocumentUri); + + // Can't proceed without the remote document + if (!newDoc) { + throwRuntimeError("Failed to fetch referenced schema document: " + *actualDocumentUri); + } + + typedef typename DocumentCache::Type::value_type + DocCacheValueType; + + docCache.insert(DocCacheValueType(*actualDocumentUri, newDoc)); + + } else { + newDoc = docCacheItr->second; + } + + const AdapterType newRootNode(*newDoc); + + // Find where we need to be in the document + const AdapterType &referencedAdapter = + internal::json_pointer::resolveJsonPointer(newRootNode, + actualJsonPointer); + + newCacheKeys.push_back(queryKey); + + // Populate the schema, starting from the referenced node, with + // nested JSON References resolved relative to the new root node + return makeOrReuseSchema(rootSchema, newRootNode, referencedAdapter, + currentScope, actualJsonPointer, fetchDoc, parentSubschema, + ownName, docCache, schemaCache, newCacheKeys); + + } + + // JSON References in nested schema will be resolved relative to the + // current document + const AdapterType &referencedAdapter = + internal::json_pointer::resolveJsonPointer( + rootNode, actualJsonPointer); + + newCacheKeys.push_back(queryKey); + + // Populate the schema, starting from the referenced node, with + // nested JSON References resolved relative to the new root node + return makeOrReuseSchema(rootSchema, rootNode, referencedAdapter, + currentScope, actualJsonPointer, fetchDoc, parentSubschema, + ownName, docCache, schemaCache, newCacheKeys); + } + + /** + * @brief Return pointer for the schema corresponding to a given node + * + * This function makes use of a schema cache, so that if the path to the + * current node is the same as one that has already been parsed and + * populated, a pointer to the existing Subschema will be returned. + * + * Should a series of $ref, or reference, nodes be resolved before reaching + * a concrete node, an entry will be added to the schema cache for each of + * the nodes in that path. + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and + * modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document + * @param node Reference to the node to parse + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param parentSchema Optional pointer to the parent schema, used to + * support required keyword in Draft 3 + * @param ownName Optional pointer to a node name, used to support + * the 'required' keyword in Draft 3 + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + */ + template + const Subschema * makeOrReuseSchema( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + const Subschema *parentSubschema, + const std::string *ownName, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + std::vector schemaCacheKeysToCreate; + + return makeOrReuseSchema(rootSchema, rootNode, node, currentScope, + nodePath, fetchDoc, parentSubschema, ownName, docCache, + schemaCache, schemaCacheKeysToCreate); + } + + /** + * @brief Populate a Schema object from JSON Schema document + * + * When processing Draft 3 schemas, the parentSubschema and ownName pointers + * should be set in contexts where a 'required' constraint would be valid. + * These are used to add a RequiredConstraint object to the Schema that + * contains the required property. + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and + * modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document + * @param node Reference to node to parse + * @param schema Reference to Schema to populate + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Optional function to fetch remote JSON documents + * @param parentSubschema Optional pointer to the parent schema, used to + * support required keyword in Draft 3 + * @param ownName Optional pointer to a node name, used to support + * the 'required' keyword in Draft 3 + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + */ + template + void populateSchema( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const Subschema &subschema, + const opt::optional& currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + const Subschema *parentSubschema, + const std::string *ownName, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + static_assert((std::is_convertible::value), + "SchemaParser::populateSchema must be invoked with an " + "appropriate Adapter implementation"); + + if (!node.isObject()) { + if (m_version == kDraft7 && node.maybeBool()) { + // Boolean schema + if (!node.asBool()) { + rootSchema.setAlwaysInvalid(&subschema, true); + } + return; + } else { + std::string s; + s += "Expected node at "; + s += nodePath; + if (m_version == kDraft7) { + s += " to contain schema object or boolean value; actual node type is: "; + } else { + s += " to contain schema object; actual node type is: "; + } + s += internal::nodeTypeAsString(node); + throwRuntimeError(s); + } + } + + const typename AdapterType::Object object = node.asObject(); + typename AdapterType::Object::const_iterator itr(object.end()); + + // Check for 'id' attribute and update current scope + opt::optional updatedScope; + if ((itr = object.find("id")) != object.end() && itr->second.maybeString()) { + const std::string id = itr->second.asString(); + rootSchema.setSubschemaId(&subschema, itr->second.asString()); + if (!currentScope || internal::uri::isUriAbsolute(id)) { + updatedScope = id; + } else { + updatedScope = internal::uri::resolveRelativeUri(*currentScope, id); + } + } else { + updatedScope = currentScope; + } + + if ((itr = object.find("allOf")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeAllOfConstraint(rootSchema, rootNode, itr->second, + updatedScope, nodePath + "/allOf", fetchDoc, + docCache, schemaCache), + &subschema); + } + + if ((itr = object.find("anyOf")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeAnyOfConstraint(rootSchema, rootNode, itr->second, + updatedScope, nodePath + "/anyOf", fetchDoc, + docCache, schemaCache), + &subschema); + } + + if ((itr = object.find("const")) != object.end()) { + rootSchema.addConstraintToSubschema(makeConstConstraint(itr->second), &subschema); + } + + if ((itr = object.find("contains")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeContainsConstraint(rootSchema, rootNode, itr->second, + updatedScope, nodePath + "/contains", fetchDoc, + docCache, schemaCache), &subschema); + } + + if ((itr = object.find("dependencies")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeDependenciesConstraint(rootSchema, rootNode, + itr->second, updatedScope, + nodePath + "/dependencies", fetchDoc, docCache, + schemaCache), + &subschema); + } + + if ((itr = object.find("description")) != object.end()) { + if (itr->second.maybeString()) { + rootSchema.setSubschemaDescription(&subschema, + itr->second.asString()); + } else { + throwRuntimeError( + "'description' attribute should have a string value"); + } + } + + if ((itr = object.find("divisibleBy")) != object.end()) { + if (m_version == kDraft3) { + if (itr->second.maybeInteger()) { + rootSchema.addConstraintToSubschema( + makeMultipleOfIntConstraint(itr->second), + &subschema); + } else if (itr->second.maybeDouble()) { + rootSchema.addConstraintToSubschema( + makeMultipleOfDoubleConstraint(itr->second), + &subschema); + } else { + throwRuntimeError("Expected an numeric value for " + " 'divisibleBy' constraint."); + } + } else { + throwRuntimeError( + "'divisibleBy' constraint not valid after draft 3"); + } + } + + if ((itr = object.find("enum")) != object.end()) { + rootSchema.addConstraintToSubschema(makeEnumConstraint(itr->second), &subschema); + } + + { + const typename AdapterType::Object::const_iterator itemsItr = + object.find("items"); + + if (object.end() != itemsItr) { + if (!itemsItr->second.isArray()) { + rootSchema.addConstraintToSubschema( + makeSingularItemsConstraint(rootSchema, rootNode, + itemsItr->second, updatedScope, + nodePath + "/items", fetchDoc, docCache, + schemaCache), + &subschema); + + } else { + const typename AdapterType::Object::const_iterator + additionalItemsItr = object.find("additionalItems"); + rootSchema.addConstraintToSubschema( + makeLinearItemsConstraint(rootSchema, rootNode, + itemsItr != object.end() ? &itemsItr->second : nullptr, + additionalItemsItr != object.end() ? &additionalItemsItr->second : nullptr, + updatedScope, nodePath + "/items", + nodePath + "/additionalItems", fetchDoc, + docCache, schemaCache), + &subschema); + } + } + } + + { + const typename AdapterType::Object::const_iterator ifItr = object.find("if"); + const typename AdapterType::Object::const_iterator thenItr = object.find("then"); + const typename AdapterType::Object::const_iterator elseItr = object.find("else"); + + if (object.end() != ifItr) { + if (m_version == kDraft7) { + rootSchema.addConstraintToSubschema( + makeConditionalConstraint(rootSchema, rootNode, + ifItr->second, + thenItr == object.end() ? nullptr : &thenItr->second, + elseItr == object.end() ? nullptr : &elseItr->second, + updatedScope, nodePath, fetchDoc, docCache, schemaCache), + &subschema); + } else { + throwRuntimeError("Not supported"); + } + } + } + + if (m_version == kDraft7) { + if ((itr = object.find("exclusiveMaximum")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMaximumConstraintExclusive(itr->second), + &subschema); + } + + if ((itr = object.find("maximum")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMaximumConstraint(itr->second, nullptr), + &subschema); + } + } else if ((itr = object.find("maximum")) != object.end()) { + typename AdapterType::Object::const_iterator exclusiveMaximumItr = + object.find("exclusiveMaximum"); + if (exclusiveMaximumItr == object.end()) { + rootSchema.addConstraintToSubschema( + makeMaximumConstraint(itr->second, nullptr), + &subschema); + } else { + rootSchema.addConstraintToSubschema( + makeMaximumConstraint(itr->second, &exclusiveMaximumItr->second), + &subschema); + } + } else if (object.find("exclusiveMaximum") != object.end()) { + throwRuntimeError("'exclusiveMaximum' constraint only valid if a 'maximum' " + "constraint is also present"); + } + + if ((itr = object.find("maxItems")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMaxItemsConstraint(itr->second), &subschema); + } + + if ((itr = object.find("maxLength")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMaxLengthConstraint(itr->second), &subschema); + } + + if ((itr = object.find("maxProperties")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMaxPropertiesConstraint(itr->second), &subschema); + } + + if (m_version == kDraft7) { + if ((itr = object.find("exclusiveMinimum")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMinimumConstraintExclusive(itr->second), &subschema); + } + + if ((itr = object.find("minimum")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMinimumConstraint(itr->second, nullptr), + &subschema); + } + } else if ((itr = object.find("minimum")) != object.end()) { + typename AdapterType::Object::const_iterator exclusiveMinimumItr = object.find("exclusiveMinimum"); + if (exclusiveMinimumItr == object.end()) { + rootSchema.addConstraintToSubschema( + makeMinimumConstraint(itr->second, nullptr), + &subschema); + } else { + rootSchema.addConstraintToSubschema( + makeMinimumConstraint(itr->second, &exclusiveMinimumItr->second), + &subschema); + } + } else if (object.find("exclusiveMinimum") != object.end()) { + throwRuntimeError("'exclusiveMinimum' constraint only valid if a 'minimum' " + "constraint is also present"); + } + + if ((itr = object.find("minItems")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMinItemsConstraint(itr->second), &subschema); + } + + if ((itr = object.find("minLength")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMinLengthConstraint(itr->second), &subschema); + } + + if ((itr = object.find("minProperties")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeMinPropertiesConstraint(itr->second), &subschema); + } + + if ((itr = object.find("multipleOf")) != object.end()) { + if (m_version == kDraft3) { + throwRuntimeError("'multipleOf' constraint not available in draft 3"); + } else if (itr->second.maybeInteger()) { + rootSchema.addConstraintToSubschema( + makeMultipleOfIntConstraint(itr->second), + &subschema); + } else if (itr->second.maybeDouble()) { + rootSchema.addConstraintToSubschema( + makeMultipleOfDoubleConstraint(itr->second), + &subschema); + } else { + throwRuntimeError("Expected an numeric value for 'divisibleBy' constraint."); + } + } + + if ((itr = object.find("not")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeNotConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/not", fetchDoc, + docCache, schemaCache), + &subschema); + } + + if ((itr = object.find("oneOf")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeOneOfConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/oneOf", fetchDoc, + docCache, schemaCache), + &subschema); + } + + if ((itr = object.find("pattern")) != object.end()) { + rootSchema.addConstraintToSubschema( + makePatternConstraint(itr->second), &subschema); + } + + { + // Check for schema keywords that require the creation of a + // PropertiesConstraint instance. + const typename AdapterType::Object::const_iterator + propertiesItr = object.find("properties"), + patternPropertiesItr = object.find("patternProperties"), + additionalPropertiesItr = object.find("additionalProperties"); + if (object.end() != propertiesItr || + object.end() != patternPropertiesItr || + object.end() != additionalPropertiesItr) { + rootSchema.addConstraintToSubschema( + makePropertiesConstraint(rootSchema, rootNode, + propertiesItr != object.end() ? &propertiesItr->second : nullptr, + patternPropertiesItr != object.end() ? &patternPropertiesItr->second : nullptr, + additionalPropertiesItr != object.end() ? &additionalPropertiesItr->second : nullptr, + updatedScope, nodePath + "/properties", + nodePath + "/patternProperties", + nodePath + "/additionalProperties", + fetchDoc, &subschema, docCache, schemaCache), + &subschema); + } + } + + if ((itr = object.find("propertyNames")) != object.end()) { + if (m_version == kDraft7) { + rootSchema.addConstraintToSubschema( + makePropertyNamesConstraint(rootSchema, rootNode, itr->second, updatedScope, + nodePath, fetchDoc, docCache, schemaCache), + &subschema); + } else { + throwRuntimeError("Not supported"); + } + } + + if ((itr = object.find("required")) != object.end()) { + if (m_version == kDraft3) { + if (parentSubschema && ownName) { + opt::optional constraint = + makeRequiredConstraintForSelf(itr->second, *ownName); + if (constraint) { + rootSchema.addConstraintToSubschema(*constraint, parentSubschema); + } + } else { + throwRuntimeError("'required' constraint not valid here"); + } + } else { + rootSchema.addConstraintToSubschema(makeRequiredConstraint(itr->second), &subschema); + } + } + + if ((itr = object.find("title")) != object.end()) { + if (itr->second.maybeString()) { + rootSchema.setSubschemaTitle(&subschema, itr->second.asString()); + } else { + throwRuntimeError("'title' attribute should have a string value"); + } + } + + if ((itr = object.find("type")) != object.end()) { + rootSchema.addConstraintToSubschema( + makeTypeConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/type", fetchDoc, + docCache, schemaCache), + &subschema); + } + + if ((itr = object.find("uniqueItems")) != object.end()) { + opt::optional constraint = makeUniqueItemsConstraint(itr->second); + if (constraint) { + rootSchema.addConstraintToSubschema(*constraint, &subschema); + } + } + + for (const auto & constraintBuilder : constraintBuilders) { + if ((itr = object.find(constraintBuilder.first)) != object.end()) { + constraints::Constraint *constraint = nullptr; +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + constraint = constraintBuilder.second->make(itr->second); + rootSchema.addConstraintToSubschema(*constraint, &subschema); + delete constraint; +#if VALIJSON_USE_EXCEPTIONS + } catch (...) { + delete constraint; + throw; + } +#endif + } + } + } + + /** + * @brief Resolves a chain of JSON References before populating a schema + * + * This helper function is used directly by the publicly visible + * populateSchema function. It ensures that the node being parsed is a + * concrete node, and not a JSON Reference. This function will call itself + * recursively to resolve references until a concrete node is found. + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document + * @param node Reference to node to parse + * @param subschema Reference to Schema to populate + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param parentSchema Optional pointer to the parent schema, used to + * support required keyword in Draft 3 + * @param ownName Optional pointer to a node name, used to support + * the 'required' keyword in Draft 3 + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + */ + template + void resolveThenPopulateSchema( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const Subschema &subschema, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + const Subschema *parentSchema, + const std::string *ownName, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + std::string jsonRef; + if (!extractJsonReference(node, jsonRef)) { + populateSchema(rootSchema, rootNode, node, subschema, currentScope, nodePath, fetchDoc, parentSchema, + ownName, docCache, schemaCache); + return; + } + + // Returns a document URI if the reference points somewhere + // other than the current document + const opt::optional documentUri = internal::json_reference::getJsonReferenceUri(jsonRef); + + // Extract JSON Pointer from JSON Reference + const std::string actualJsonPointer = sanitiseJsonPointer( + internal::json_reference::getJsonReferencePointer(jsonRef)); + + if (documentUri && internal::uri::isUriAbsolute(*documentUri)) { + // Resolve reference against remote document + if (!fetchDoc) { + throwRuntimeError("Fetching of remote JSON References not enabled."); + } + + const typename DocumentCache::DocumentType *newDoc = fetchDoc(*documentUri); + + // Can't proceed without the remote document + if (!newDoc) { + throwRuntimeError("Failed to fetch referenced schema document: " + *documentUri); + } + + // Add to document cache + typedef typename DocumentCache::Type::value_type DocCacheValueType; + + docCache.insert(DocCacheValueType(*documentUri, newDoc)); + + const AdapterType newRootNode(*newDoc); + + const AdapterType &referencedAdapter = + internal::json_pointer::resolveJsonPointer(newRootNode, actualJsonPointer); + + // TODO: Need to detect degenerate circular references + resolveThenPopulateSchema(rootSchema, newRootNode, referencedAdapter, subschema, {}, actualJsonPointer, + fetchDoc, parentSchema, ownName, docCache, schemaCache); + + } else { + const AdapterType &referencedAdapter = + internal::json_pointer::resolveJsonPointer(rootNode, actualJsonPointer); + + // TODO: Need to detect degenerate circular references + resolveThenPopulateSchema(rootSchema, rootNode, referencedAdapter, subschema, {}, actualJsonPointer, + fetchDoc, parentSchema, ownName, docCache, schemaCache); + } + } + + /** + * @brief Make a new AllOfConstraint object + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document; used for recursive parsing of schemas + * @param node JSON node containing an array of child schemas + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new AllOfConstraint object that belongs to the + * caller + */ + template + constraints::AllOfConstraint makeAllOfConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + if (!node.maybeArray()) { + throwRuntimeError("Expected array value for 'allOf' constraint."); + } + + constraints::AllOfConstraint constraint; + + int index = 0; + for (const AdapterType schemaNode : node.asArray()) { + if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) { + const std::string childPath = nodePath + "/" + std::to_string(index); + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, schemaNode, currentScope, + childPath, fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.addSubschema(subschema); + index++; + } else { + throwRuntimeError("Expected element to be a valid schema in 'allOf' constraint."); + } + } + + return constraint; + } + + /** + * @brief Make a new AnyOfConstraint object + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document; used for recursive parsing of schemas + * @param node JSON node containing an array of child schemas + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new AnyOfConstraint object that belongs to the + * caller + */ + template + constraints::AnyOfConstraint makeAnyOfConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + if (!node.maybeArray()) { + throwRuntimeError("Expected array value for 'anyOf' constraint."); + } + + constraints::AnyOfConstraint constraint; + + int index = 0; + for (const AdapterType schemaNode : node.asArray()) { + if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) { + const std::string childPath = nodePath + "/" + std::to_string(index); + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, schemaNode, currentScope, + childPath, fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.addSubschema(subschema); + index++; + } else { + throwRuntimeError("Expected array element to be a valid schema in 'anyOf' constraint."); + } + } + + return constraint; + } + + /** + * @brief Make a new ConditionalConstraint object. + * + * @param rootSchema The Schema instance, and root subschema, + * through which other subschemas can be + * created and modified + * @param rootNode Reference to the node from which JSON + * References will be resolved when they refer + * to the current document; used for recursive + * parsing of schemas + * @param ifNode Schema that will be used to evaluate the + * conditional. + * @param thenNode Optional pointer to a JSON node containing + * a schema that will be used when the conditional + * evaluates to true. + * @param elseNode Optional pointer to a JSON node containing + * a schema that will be used when the conditional + * evaluates to false. + * @param currentScope URI for current resolution scope + * @param containsPath JSON Pointer representing the path to + * the 'contains' node + * @param fetchDoc Function to fetch remote JSON documents + * (optional) + * @param docCache Cache of resolved and fetched remote + * documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new ContainsConstraint that belongs to the caller + */ + template + constraints::ConditionalConstraint makeConditionalConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &ifNode, + const AdapterType *thenNode, + const AdapterType *elseNode, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + constraints::ConditionalConstraint constraint; + + const Subschema *ifSubschema = makeOrReuseSchema( + rootSchema, rootNode, ifNode, currentScope, + nodePath + "/if", fetchDoc, nullptr, nullptr, docCache, + schemaCache); + constraint.setIfSubschema(ifSubschema); + + if (thenNode) { + const Subschema *thenSubschema = makeOrReuseSchema( + rootSchema, rootNode, *thenNode, currentScope, nodePath + "/then", fetchDoc, nullptr, + nullptr, docCache, schemaCache); + constraint.setThenSubschema(thenSubschema); + } + + if (elseNode) { + const Subschema *elseSubschema = makeOrReuseSchema( + rootSchema, rootNode, *elseNode, currentScope, nodePath + "/else", fetchDoc, nullptr, + nullptr, docCache, schemaCache); + constraint.setElseSubschema(elseSubschema); + } + + return constraint; + } + + /** + * @brief Make a new ConstConstraint object. + * + * @param node JSON node containing an arbitrary value + * + * @return pointer to a new MinimumConstraint that belongs to the caller + */ + template + constraints::ConstConstraint makeConstConstraint(const AdapterType &node) + { + constraints::ConstConstraint constraint; + constraint.setValue(node); + return constraint; + } + + /** + * @brief Make a new ContainsConstraint object. + * + * @param rootSchema The Schema instance, and root subschema, + * through which other subschemas can be + * created and modified + * @param rootNode Reference to the node from which JSON + * References will be resolved when they refer + * to the current document; used for recursive + * parsing of schemas + * @param contains Optional pointer to a JSON node containing + * an object mapping property names to + * schemas. + * @param currentScope URI for current resolution scope + * @param containsPath JSON Pointer representing the path to + * the 'contains' node + * @param fetchDoc Function to fetch remote JSON documents + * (optional) + * @param docCache Cache of resolved and fetched remote + * documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new ContainsConstraint that belongs to the caller + */ + template + constraints::ContainsConstraint makeContainsConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &contains, + const opt::optional currentScope, + const std::string &containsPath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + constraints::ContainsConstraint constraint; + + if (contains.isObject() || (m_version == kDraft7 && contains.maybeBool())) { + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, contains, currentScope, containsPath, + fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.setSubschema(subschema); + + } else if (contains.maybeObject()) { + // If a loosely-typed Adapter type is being used, then we'll + // assume that an empty schema has been provided. + constraint.setSubschema(rootSchema.emptySubschema()); + + } else { + // All other formats will result in an exception being thrown. + throwRuntimeError("Expected valid schema for 'contains' constraint."); + } + + return constraint; + } + + /** + * @brief Make a new DependenciesConstraint object + * + * The dependencies for a property can be defined several ways. When parsing + * a Draft 4 schema, the following can be used: + * - an array that lists the name of each property that must be present + * if the dependent property is present + * - an object that specifies a schema which must be satisfied if the + * dependent property is present + * + * When parsing a Draft 3 schema, in addition to the formats above, the + * following format can be used: + * - a string that names a single property that must be present if the + * dependent property is presnet + * + * Multiple methods can be used in the same dependency constraint. + * + * If the format of any part of the the dependency node does not match one + * of these formats, an exception will be thrown. + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document; used for recursive parsing of schemas + * @param node JSON node containing an object that defines a + * mapping of properties to their dependencies. + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new DependencyConstraint that belongs to the + * caller + */ + template + constraints::DependenciesConstraint makeDependenciesConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + if (!node.maybeObject()) { + throwRuntimeError("Expected valid subschema for 'dependencies' constraint."); + } + + constraints::DependenciesConstraint dependenciesConstraint; + + // Process each of the dependency mappings defined by the object + for (const typename AdapterType::ObjectMember member : node.asObject()) { + + // First, we attempt to parse the value of the dependency mapping + // as an array of strings. If the Adapter type does not support + // strict types, then an empty string or empty object will be cast + // to an array, and the resulting dependency list will be empty. + // This is equivalent to using an empty object, but does mean that + // if the user provides an actual string then this error will not + // be detected. + if (member.second.maybeArray()) { + // Parse an array of dependency names + std::vector dependentPropertyNames; + for (const AdapterType dependencyName : member.second.asArray()) { + if (dependencyName.maybeString()) { + dependentPropertyNames.push_back(dependencyName.getString()); + } else { + throwRuntimeError("Expected string value in dependency list of property '" + + member.first + "' in 'dependencies' constraint."); + } + } + + dependenciesConstraint.addPropertyDependencies(member.first, + dependentPropertyNames); + + // If the value of dependency mapping could not be processed as an + // array, we'll try to process it as an object instead. Note that + // strict type comparison is used here, since we've already + // exercised the flexibility by loosely-typed Adapter types. If the + // value of the dependency mapping is an object, then we'll try to + // process it as a dependent schema. + } else if (member.second.isObject() || (m_version == kDraft7 && member.second.maybeBool())) { + // Parse dependent subschema + const Subschema *childSubschema = + makeOrReuseSchema(rootSchema, rootNode, + member.second, currentScope, nodePath, fetchDoc, + nullptr, nullptr, docCache, schemaCache); + dependenciesConstraint.addSchemaDependency(member.first, + childSubschema); + + // If we're supposed to be parsing a Draft3 schema, then the value + // of the dependency mapping can also be a string containing the + // name of a single dependency. + } else if (m_version == kDraft3 && member.second.isString()) { + dependenciesConstraint.addPropertyDependency(member.first, + member.second.getString()); + + // All other types result in an exception being thrown. + } else { + throwRuntimeError("Invalid dependencies definition."); + } + } + + return dependenciesConstraint; + } + + /** + * @brief Make a new EnumConstraint object. + * + * @param node JSON node containing an array of values permitted by the + * constraint. + * + * @return pointer to a new EnumConstraint that belongs to the caller + */ + template + constraints::EnumConstraint makeEnumConstraint( + const AdapterType &node) + { + // Make a copy of each value in the enum array + constraints::EnumConstraint constraint; + for (const AdapterType value : node.getArray()) { + constraint.addValue(value); + } + + /// @todo This will make another copy of the values while constructing + /// the EnumConstraint. Move semantics in C++11 should make it possible + /// to avoid these copies without complicating the implementation of the + /// EnumConstraint class. + return constraint; + } + + /** + * @brief Make a new ItemsConstraint object. + * + * @param rootSchema The Schema instance, and root subschema, + * through which other subschemas can be + * created and modified + * @param rootNode Reference to the node from which JSON + * References will be resolved when they refer + * to the current document; used for recursive + * parsing of schemas + * @param items Optional pointer to a JSON node containing + * an object mapping property names to + * schemas. + * @param additionalItems Optional pointer to a JSON node containing + * an additional properties schema or a + * boolean value. + * @param currentScope URI for current resolution scope + * @param itemsPath JSON Pointer representing the path to + * the 'items' node + * @param additionalItemsPath JSON Pointer representing the path to + * the 'additionalItems' node + * @param fetchDoc Function to fetch remote JSON documents + * (optional) + * @param docCache Cache of resolved and fetched remote + * documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new ItemsConstraint that belongs to the caller + */ + template + constraints::LinearItemsConstraint makeLinearItemsConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType *items, + const AdapterType *additionalItems, + const opt::optional currentScope, + const std::string &itemsPath, + const std::string &additionalItemsPath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + constraints::LinearItemsConstraint constraint; + + // Construct a Schema object for the the additionalItems constraint, + // if the additionalItems property is present + if (additionalItems) { + if (additionalItems->maybeBool()) { + // If the value of the additionalItems property is a boolean + // and is set to true, then additional array items do not need + // to satisfy any constraints. + if (additionalItems->asBool()) { + constraint.setAdditionalItemsSubschema(rootSchema.emptySubschema()); + } + } else if (additionalItems->maybeObject()) { + // If the value of the additionalItems property is an object, + // then it should be parsed into a Schema object, which will be + // used to validate additional array items. + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, *additionalItems, currentScope, + additionalItemsPath, fetchDoc, nullptr, nullptr, docCache, + schemaCache); + constraint.setAdditionalItemsSubschema(subschema); + } else { + // Any other format for the additionalItems property will result + // in an exception being thrown. + throwRuntimeError("Expected bool or object value for 'additionalItems'"); + } + } else { + // The default value for the additionalItems property is an empty + // object, which means that additional array items do not need to + // satisfy any constraints. + constraint.setAdditionalItemsSubschema(rootSchema.emptySubschema()); + } + + // Construct a Schema object for each item in the items array. + // If the items constraint is not provided, then array items + // will be validated against the additionalItems schema. + if (items) { + if (items->isArray()) { + // If the items constraint contains an array, then it should + // contain a list of child schemas which will be used to + // validate the values at the corresponding indexes in a target + // array. + int index = 0; + for (const AdapterType v : items->getArray()) { + const std::string childPath = itemsPath + "/" + + std::to_string(index); + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, v, currentScope, childPath, + fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.addItemSubschema(subschema); + index++; + } + } else { + throwRuntimeError("Expected array value for non-singular 'items' constraint."); + } + } + + return constraint; + } + + /** + * @brief Make a new ItemsConstraint object. + * + * @param rootSchema The Schema instance, and root subschema, + * through which other subschemas can be + * created and modified + * @param rootNode Reference to the node from which JSON + * References will be resolved when they refer + * to the current document; used for recursive + * parsing of schemas + * @param items Optional pointer to a JSON node containing + * an object mapping property names to + * schemas. + * @param additionalItems Optional pointer to a JSON node containing + * an additional properties schema or a + * boolean value. + * @param currentScope URI for current resolution scope + * @param itemsPath JSON Pointer representing the path to + * the 'items' node + * @param additionalItemsPath JSON Pointer representing the path to + * the 'additionalItems' node + * @param fetchDoc Function to fetch remote JSON documents + * (optional) + * @param docCache Cache of resolved and fetched remote + * documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new ItemsConstraint that belongs to the caller + */ + template + constraints::SingularItemsConstraint makeSingularItemsConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &items, + const opt::optional currentScope, + const std::string &itemsPath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + constraints::SingularItemsConstraint constraint; + + // Construct a Schema object for each item in the items array, if an + // array is provided, or a single Schema object, in an object value is + // provided. If the items constraint is not provided, then array items + // will be validated against the additionalItems schema. + if (items.isObject() || (m_version == kDraft7 && items.maybeBool())) { + // If the items constraint contains an object value, then it + // should contain a Schema that will be used to validate all + // items in a target array. Any schema defined by the + // additionalItems constraint will be ignored. + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, items, currentScope, itemsPath, + fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.setItemsSubschema(subschema); + + } else if (items.maybeObject()) { + // If a loosely-typed Adapter type is being used, then we'll + // assume that an empty schema has been provided. + constraint.setItemsSubschema(rootSchema.emptySubschema()); + + } else { + // All other formats will result in an exception being thrown. + throwRuntimeError("Expected valid schema for singular 'items' constraint."); + } + + return constraint; + } + + /** + * @brief Make a new MaximumConstraint object (draft 3 and 4). + * + * @param rootSchema The Schema instance, and root subschema, + * through which other subschemas can be + * created and modified + * @param rootNode Reference to the node from which JSON + * References will be resolved when they refer + * to the current document; used for recursive + * parsing of schemas + * @param node JSON node containing the maximum value. + * @param exclusiveMaximum Optional pointer to a JSON boolean value that + * indicates whether maximum value is excluded + * from the range of permitted values. + * + * @return pointer to a new MaximumConstraint that belongs to the caller + */ + template + constraints::MaximumConstraint makeMaximumConstraint( + const AdapterType &node, + const AdapterType *exclusiveMaximum) + { + if (!node.maybeDouble()) { + throwRuntimeError("Expected numeric value for maximum constraint."); + } + + constraints::MaximumConstraint constraint; + constraint.setMaximum(node.asDouble()); + + if (exclusiveMaximum) { + if (!exclusiveMaximum->maybeBool()) { + throwRuntimeError("Expected boolean value for exclusiveMaximum constraint."); + } + + constraint.setExclusiveMaximum(exclusiveMaximum->asBool()); + } + + return constraint; + } + + /** + * @brief Make a new MaximumConstraint object that is always exclusive (draft 7). + * + * @param node JSON node containing an integer, representing the maximum value. + * + * @param exclusive Optional pointer to a JSON boolean value that indicates whether the + * maximum value is excluded from the range of permitted values. + * + * @return pointer to a new Maximum that belongs to the caller + */ + template + constraints::MaximumConstraint makeMaximumConstraintExclusive(const AdapterType &node) + { + if (!node.maybeDouble()) { + throwRuntimeError("Expected numeric value for exclusiveMaximum constraint."); + } + + constraints::MaximumConstraint constraint; + constraint.setMaximum(node.asDouble()); + constraint.setExclusiveMaximum(true); + return constraint; + } + + /** + * @brief Make a new MaxItemsConstraint object. + * + * @param node JSON node containing an integer value representing the + * maximum number of items that may be contaned by an array. + * + * @return pointer to a new MaxItemsConstraint that belongs to the caller. + */ + template + constraints::MaxItemsConstraint makeMaxItemsConstraint( + const AdapterType &node) + { + if (node.maybeInteger()) { + const int64_t value = node.asInteger(); + if (value >= 0) { + constraints::MaxItemsConstraint constraint; + constraint.setMaxItems(value); + return constraint; + } + } + + throwRuntimeError("Expected non-negative integer value for 'maxItems' constraint."); + } + + /** + * @brief Make a new MaxLengthConstraint object. + * + * @param node JSON node containing an integer value representing the + * maximum length of a string. + * + * @return pointer to a new MaxLengthConstraint that belongs to the caller + */ + template + constraints::MaxLengthConstraint makeMaxLengthConstraint( + const AdapterType &node) + { + if (node.maybeInteger()) { + const int64_t value = node.asInteger(); + if (value >= 0) { + constraints::MaxLengthConstraint constraint; + constraint.setMaxLength(value); + return constraint; + } + } + + throwRuntimeError("Expected a non-negative integer value for 'maxLength' constraint."); + } + + /** + * @brief Make a new MaxPropertiesConstraint object. + * + * @param node JSON node containing an integer value representing the + * maximum number of properties that may be contained by an + * object. + * + * @return pointer to a new MaxPropertiesConstraint that belongs to the + * caller + */ + template + constraints::MaxPropertiesConstraint makeMaxPropertiesConstraint( + const AdapterType &node) + { + if (node.maybeInteger()) { + int64_t value = node.asInteger(); + if (value >= 0) { + constraints::MaxPropertiesConstraint constraint; + constraint.setMaxProperties(value); + return constraint; + } + } + + throwRuntimeError("Expected a non-negative integer for 'maxProperties' constraint."); + } + + /** + * @brief Make a new MinimumConstraint object (draft 3 and 4). + * + * @param node JSON node containing an integer, representing + * the minimum value. + * + * @param exclusiveMaximum Optional pointer to a JSON boolean value that + * indicates whether the minimum value is + * excluded from the range of permitted values. + * + * @return pointer to a new MinimumConstraint that belongs to the caller + */ + template + constraints::MinimumConstraint makeMinimumConstraint( + const AdapterType &node, + const AdapterType *exclusiveMinimum) + { + if (!node.maybeDouble()) { + throwRuntimeError("Expected numeric value for minimum constraint."); + } + + constraints::MinimumConstraint constraint; + constraint.setMinimum(node.asDouble()); + + if (exclusiveMinimum) { + if (!exclusiveMinimum->maybeBool()) { + throwRuntimeError("Expected boolean value for 'exclusiveMinimum' constraint."); + } + + constraint.setExclusiveMinimum(exclusiveMinimum->asBool()); + } + + return constraint; + } + + /** + * @brief Make a new MinimumConstraint object that is always exclusive (draft 7). + * + * @param node JSON node containing an integer, representing the minimum value. + * + * @param exclusive Optional pointer to a JSON boolean value that indicates whether the + * minimum value is excluded from the range of permitted values. + * + * @return pointer to a new MinimumConstraint that belongs to the caller + */ + template + constraints::MinimumConstraint makeMinimumConstraintExclusive(const AdapterType &node) + { + if (!node.maybeDouble()) { + throwRuntimeError("Expected numeric value for exclusiveMinimum constraint."); + } + + constraints::MinimumConstraint constraint; + constraint.setMinimum(node.asDouble()); + constraint.setExclusiveMinimum(true); + return constraint; + } + + /** + * @brief Make a new MinItemsConstraint object. + * + * @param node JSON node containing an integer value representing the + * minimum number of items that may be contained by an array. + * + * @return pointer to a new MinItemsConstraint that belongs to the caller + */ + template + constraints::MinItemsConstraint makeMinItemsConstraint(const AdapterType &node) + { + if (node.maybeInteger()) { + const int64_t value = node.asInteger(); + if (value >= 0) { + constraints::MinItemsConstraint constraint; + constraint.setMinItems(value); + return constraint; + } + } + + throwRuntimeError("Expected a non-negative integer value for 'minItems' constraint."); + } + + /** + * @brief Make a new MinLengthConstraint object. + * + * @param node JSON node containing an integer value representing the + * minimum length of a string. + * + * @return pointer to a new MinLengthConstraint that belongs to the caller + */ + template + constraints::MinLengthConstraint makeMinLengthConstraint(const AdapterType &node) + { + if (node.maybeInteger()) { + const int64_t value = node.asInteger(); + if (value >= 0) { + constraints::MinLengthConstraint constraint; + constraint.setMinLength(value); + return constraint; + } + } + + throwRuntimeError("Expected a non-negative integer value for 'minLength' constraint."); + } + + + /** + * @brief Make a new MaxPropertiesConstraint object. + * + * @param node JSON node containing an integer value representing the + * minimum number of properties that may be contained by an + * object. + * + * @return pointer to a new MinPropertiesConstraint that belongs to the + * caller + */ + template + constraints::MinPropertiesConstraint makeMinPropertiesConstraint(const AdapterType &node) + { + if (node.maybeInteger()) { + int64_t value = node.asInteger(); + if (value >= 0) { + constraints::MinPropertiesConstraint constraint; + constraint.setMinProperties(value); + return constraint; + } + } + + throwRuntimeError("Expected a non-negative integer for 'minProperties' constraint."); + } + + /** + * @brief Make a new MultipleOfDoubleConstraint object + * + * @param node JSON node containing an numeric value that a target value + * must divide by in order to satisfy this constraint + * + * @return a MultipleOfConstraint + */ + template + constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint(const AdapterType &node) + { + constraints::MultipleOfDoubleConstraint constraint; + constraint.setDivisor(node.asDouble()); + return constraint; + } + + /** + * @brief Make a new MultipleOfIntConstraint object + * + * @param node JSON node containing a numeric value that a target value + * must divide by in order to satisfy this constraint + * + * @return a MultipleOfIntConstraint + */ + template + constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint(const AdapterType &node) + { + constraints::MultipleOfIntConstraint constraint; + constraint.setDivisor(node.asInteger()); + return constraint; + } + + /** + * @brief Make a new NotConstraint object + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document; used for recursive parsing of schemas + * @param node JSON node containing a schema + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new NotConstraint object that belongs to the caller + */ + template + constraints::NotConstraint makeNotConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + if (node.maybeObject() || (m_version == kDraft7 && node.maybeBool())) { + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, node, currentScope, nodePath, + fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraints::NotConstraint constraint; + constraint.setSubschema(subschema); + return constraint; + } + + throwRuntimeError("Expected object value for 'not' constraint."); + } + + /** + * @brief Make a new OneOfConstraint object + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document; used for recursive parsing of schemas + * @param node JSON node containing an array of child schemas + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new OneOfConstraint that belongs to the caller + */ + template + constraints::OneOfConstraint makeOneOfConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + constraints::OneOfConstraint constraint; + + int index = 0; + for (const AdapterType schemaNode : node.getArray()) { + const std::string childPath = nodePath + "/" + std::to_string(index); + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, schemaNode, currentScope, childPath, + fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.addSubschema(subschema); + index++; + } + + return constraint; + } + + /** + * @brief Make a new PatternConstraint object. + * + * @param node JSON node containing a pattern string + * + * @return pointer to a new PatternConstraint object that belongs to the + * caller + */ + template + constraints::PatternConstraint makePatternConstraint( + const AdapterType &node) + { + constraints::PatternConstraint constraint; + constraint.setPattern(node.getString()); + return constraint; + } + + /** + * @brief Make a new Properties object. + * + * @param rootSchema The Schema instance, and root + * subschema, through which other + * subschemas can be created and modified + * @param rootNode Reference to the node from which JSON + * References will be resolved when they + * refer to the current document; used + * for recursive parsing of schemas + * @param properties Optional pointer to a JSON node + * containing an object mapping property + * names to schemas. + * @param patternProperties Optional pointer to a JSON node + * containing an object mapping pattern + * property names to schemas. + * @param additionalProperties Optional pointer to a JSON node + * containing an additional properties + * schema or a boolean value. + * @param currentScope URI for current resolution scope + * @param propertiesPath JSON Pointer representing the path to + * the 'properties' node + * @param patternPropertiesPath JSON Pointer representing the path to + * the 'patternProperties' node + * @param additionalPropertiesPath JSON Pointer representing the path to + * the 'additionalProperties' node + * @param fetchDoc Function to fetch remote JSON + * documents (optional) + * @param parentSubschema Optional pointer to the Schema of the + * parent object, needed to support the + * 'required' keyword in Draft 3 + * @param docCache Cache of resolved and fetched remote + * documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new Properties that belongs to the caller + */ + template + constraints::PropertiesConstraint makePropertiesConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType *properties, + const AdapterType *patternProperties, + const AdapterType *additionalProperties, + const opt::optional currentScope, + const std::string &propertiesPath, + const std::string &patternPropertiesPath, + const std::string &additionalPropertiesPath, + const typename FunctionPtrs::FetchDoc fetchDoc, + const Subschema *parentSubschema, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + typedef typename AdapterType::ObjectMember Member; + + constraints::PropertiesConstraint constraint; + + // Create subschemas for 'properties' constraint + if (properties) { + for (const Member m : properties->getObject()) { + const std::string &property = m.first; + const std::string childPath = propertiesPath + "/" + property; + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, m.second, currentScope, childPath, + fetchDoc, parentSubschema, &property, docCache, + schemaCache); + constraint.addPropertySubschema(property, subschema); + } + } + + // Create subschemas for 'patternProperties' constraint + if (patternProperties) { + for (const Member m : patternProperties->getObject()) { + const std::string &pattern = m.first; + const std::string childPath = patternPropertiesPath + "/" + pattern; + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, m.second, currentScope, childPath, + fetchDoc, parentSubschema, &pattern, docCache, + schemaCache); + constraint.addPatternPropertySubschema(pattern, subschema); + } + } + + // Create an additionalItems subschema if required + if (additionalProperties) { + // If additionalProperties has been set, check for a boolean value. + // Setting 'additionalProperties' to true allows the values of + // additional properties to take any form. Setting it false + // prohibits the use of additional properties. + // If additionalProperties is instead an object, it should be + // parsed as a schema. If additionalProperties has any other type, + // then the schema is not valid. + if (additionalProperties->isBool() || + additionalProperties->maybeBool()) { + // If it has a boolean value that is 'true', then an empty + // schema should be used. + if (additionalProperties->asBool()) { + constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema()); + } + } else if (additionalProperties->isObject()) { + // If additionalProperties is an object, it should be used as + // a child schema. + const Subschema *subschema = makeOrReuseSchema( + rootSchema, rootNode, *additionalProperties, + currentScope, additionalPropertiesPath, fetchDoc, nullptr, + nullptr, docCache, schemaCache); + constraint.setAdditionalPropertiesSubschema(subschema); + } else { + // All other types are invalid + throwRuntimeError("Invalid type for 'additionalProperties' constraint."); + } + } else { + // If an additionalProperties constraint is not provided, then the + // default value is an empty schema. + constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema()); + } + + return constraint; + } + + template + constraints::PropertyNamesConstraint makePropertyNamesConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType ¤tNode, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + const Subschema *subschema = makeOrReuseSchema(rootSchema, rootNode, currentNode, currentScope, + nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraints::PropertyNamesConstraint constraint; + constraint.setSubschema(subschema); + return constraint; + } + + /** + * @brief Make a new RequiredConstraint. + * + * This function is used to create new RequiredContraint objects for + * Draft 3 schemas. + * + * @param node Node containing a boolean value. + * @param name Name of the required attribute. + * + * @return pointer to a new RequiredConstraint object that belongs to the + * caller + */ + template + opt::optional + makeRequiredConstraintForSelf(const AdapterType &node, + const std::string &name) + { + if (!node.maybeBool()) { + throwRuntimeError("Expected boolean value for 'required' attribute."); + } + + if (node.asBool()) { + constraints::RequiredConstraint constraint; + constraint.addRequiredProperty(name); + return constraint; + } + + return opt::optional(); + } + + /** + * @brief Make a new RequiredConstraint. + * + * This function is used to create new RequiredContraint objects for + * Draft 4 schemas. + * + * @param node Node containing an array of strings. + * + * @return pointer to a new RequiredConstraint object that belongs to the + * caller + */ + template + constraints::RequiredConstraint makeRequiredConstraint( + const AdapterType &node) + { + constraints::RequiredConstraint constraint; + + for (const AdapterType v : node.getArray()) { + if (!v.maybeString()) { + throwRuntimeError("Expected required property name to be a string value"); + } + + constraint.addRequiredProperty(v.getString()); + } + + return constraint; + } + + /** + * @brief Make a new TypeConstraint object + * + * @param rootSchema The Schema instance, and root subschema, through + * which other subschemas can be created and modified + * @param rootNode Reference to the node from which JSON References + * will be resolved when they refer to the current + * document; used for recursive parsing of schemas + * @param node Node containing the name of a JSON type + * @param currentScope URI for current resolution scope + * @param nodePath JSON Pointer representing path to current node + * @param fetchDoc Function to fetch remote JSON documents (optional) + * @param docCache Cache of resolved and fetched remote documents + * @param schemaCache Cache of populated schemas + * + * @return pointer to a new TypeConstraint object. + */ + template + constraints::TypeConstraint makeTypeConstraint( + Schema &rootSchema, + const AdapterType &rootNode, + const AdapterType &node, + const opt::optional currentScope, + const std::string &nodePath, + const typename FunctionPtrs::FetchDoc fetchDoc, + typename DocumentCache::Type &docCache, + SchemaCache &schemaCache) + { + typedef constraints::TypeConstraint TypeConstraint; + + TypeConstraint constraint; + + if (node.maybeString()) { + const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(node.getString()); + if (type == TypeConstraint::kAny && m_version == kDraft4) { + throwRuntimeError("'any' type is not supported in version 4 schemas."); + } + + constraint.addNamedType(type); + + } else if (node.maybeArray()) { + int index = 0; + for (const AdapterType v : node.getArray()) { + if (v.maybeString()) { + const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(v.getString()); + if (type == TypeConstraint::kAny && m_version == kDraft4) { + throwRuntimeError("'any' type is not supported in version 4 schemas."); + } + + constraint.addNamedType(type); + + } else if (v.maybeObject() && m_version == kDraft3) { + const std::string childPath = nodePath + "/" + std::to_string(index); + const Subschema *subschema = makeOrReuseSchema(rootSchema, rootNode, v, currentScope, + childPath, fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.addSchemaType(subschema); + + } else { + throwRuntimeError("Type name should be a string."); + } + + index++; + } + + } else if (node.maybeObject() && m_version == kDraft3) { + const Subschema *subschema = makeOrReuseSchema(rootSchema, rootNode, node, currentScope, + nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache); + constraint.addSchemaType(subschema); + + } else { + throwRuntimeError("Type name should be a string."); + } + + return constraint; + } + + /** + * @brief Make a new UniqueItemsConstraint object. + * + * @param node Node containing a boolean value. + * + * @return pointer to a new UniqueItemsConstraint object that belongs to + * the caller, or nullptr if the boolean value is false. + */ + template + opt::optional makeUniqueItemsConstraint(const AdapterType &node) + { + if (node.isBool() || node.maybeBool()) { + // If the boolean value is true, this function will return a pointer + // to a new UniqueItemsConstraint object. If it is value, then the + // constraint is redundant, so nullptr is returned instead. + if (node.asBool()) { + return constraints::UniqueItemsConstraint(); + } else { + return opt::optional(); + } + } + + throwRuntimeError("Expected boolean value for 'uniqueItems' constraint."); + } + +private: + + /// Version of JSON Schema that should be expected when parsing + const Version m_version; +}; + +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/subschema.hpp b/subprojects/valijson/include/valijson/subschema.hpp new file mode 100644 index 000000000..abee6c42b --- /dev/null +++ b/subprojects/valijson/include/valijson/subschema.hpp @@ -0,0 +1,311 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +namespace valijson { + +/** + * Represents a sub-schema within a JSON Schema + * + * While all JSON Schemas have at least one sub-schema, the root, some will + * have additional sub-schemas that are defined as part of constraints that are + * included in the schema. For example, a 'oneOf' constraint maintains a set of + * references to one or more nested sub-schemas. As per the definition of a + * oneOf constraint, a document is valid within that constraint if it validates + * against one of the nested sub-schemas. + */ +class Subschema +{ +public: + + /// Typedef for custom new-/malloc-like function + typedef void * (*CustomAlloc)(size_t size); + + /// Typedef for custom free-like function + typedef void (*CustomFree)(void *); + + /// Typedef the Constraint class into the local namespace for convenience + typedef constraints::Constraint Constraint; + + /// Typedef for a function that can be applied to each of the Constraint + /// instances owned by a Schema. + typedef std::function ApplyFunction; + + // Disable copy construction + Subschema(const Subschema &) = delete; + + // Disable copy assignment + Subschema & operator=(const Subschema &) = delete; + + /** + * @brief Construct a new Subschema object + */ + Subschema() + : m_allocFn(::operator new) + , m_freeFn(::operator delete) + , m_alwaysInvalid(false) { } + + /** + * @brief Construct a new Subschema using custom memory management + * functions + * + * @param allocFn malloc- or new-like function to allocate memory + * within Schema, such as for Subschema instances + * @param freeFn free-like function to free memory allocated with + * the `customAlloc` function + */ + Subschema(CustomAlloc allocFn, CustomFree freeFn) + : m_allocFn(allocFn) + , m_freeFn(freeFn) + , m_alwaysInvalid(false) + { + // explicitly initialise optionals. See: https://github.com/tristanpenman/valijson/issues/124 + m_description = opt::nullopt; + m_id = opt::nullopt; + m_title = opt::nullopt; + } + + /** + * @brief Clean up and free all memory managed by the Subschema + */ + virtual ~Subschema() + { +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + for (auto constConstraint : m_constraints) { + auto *constraint = const_cast(constConstraint); + constraint->~Constraint(); + m_freeFn(constraint); + } + m_constraints.clear(); +#if VALIJSON_USE_EXCEPTIONS + } catch (const std::exception &e) { + fprintf(stderr, "Caught an exception in Subschema destructor: %s", + e.what()); + } +#endif + } + + /** + * @brief Add a constraint to this sub-schema + * + * The constraint will be copied before being added to the list of + * constraints for this Subschema. Note that constraints will be copied + * only as deep as references to other Subschemas - e.g. copies of + * constraints that refer to sub-schemas, will continue to refer to the + * same Subschema instances. + * + * @param constraint Reference to the constraint to copy + */ + void addConstraint(const Constraint &constraint) + { + Constraint *newConstraint = constraint.clone(m_allocFn, m_freeFn); +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + m_constraints.push_back(newConstraint); +#if VALIJSON_USE_EXCEPTIONS + } catch (...) { + newConstraint->~Constraint(); + m_freeFn(newConstraint); + throw; + } +#endif + } + + /** + * @brief Invoke a function on each child Constraint + * + * This function will apply the callback function to each constraint in + * the Subschema, even if one of the invokations returns \c false. However, + * if one or more invokations of the callback function return \c false, + * this function will also return \c false. + * + * @returns \c true if all invokations of the callback function are + * successful, \c false otherwise + */ + bool apply(ApplyFunction &applyFunction) const + { + bool allTrue = true; + for (const Constraint *constraint : m_constraints) { + allTrue = allTrue && applyFunction(*constraint); + } + + return allTrue; + } + + /** + * @brief Invoke a function on each child Constraint + * + * This is a stricter version of the apply() function that will return + * immediately if any of the invokations of the callback function return + * \c false. + * + * @returns \c true if all invokations of the callback function are + * successful, \c false otherwise + */ + bool applyStrict(ApplyFunction &applyFunction) const + { + for (const Constraint *constraint : m_constraints) { + if (!applyFunction(*constraint)) { + return false; + } + } + + return true; + } + + bool getAlwaysInvalid() const + { + return m_alwaysInvalid; + } + + /** + * @brief Get the description associated with this sub-schema + * + * @throws std::runtime_error if a description has not been set + * + * @returns string containing sub-schema description + */ + std::string getDescription() const + { + if (m_description) { + return *m_description; + } + + throwRuntimeError("Schema does not have a description"); + } + + /** + * @brief Get the ID associated with this sub-schema + * + * @throws std::runtime_error if an ID has not been set + * + * @returns string containing sub-schema ID + */ + std::string getId() const + { + if (m_id) { + return *m_id; + } + + throwRuntimeError("Schema does not have an ID"); + } + + /** + * @brief Get the title associated with this sub-schema + * + * @throws std::runtime_error if a title has not been set + * + * @returns string containing sub-schema title + */ + std::string getTitle() const + { + if (m_title) { + return *m_title; + } + + throwRuntimeError("Schema does not have a title"); + } + + /** + * @brief Check whether this sub-schema has a description + * + * @return boolean value + */ + bool hasDescription() const + { + return static_cast(m_description); + } + + /** + * @brief Check whether this sub-schema has an ID + * + * @return boolean value + */ + bool hasId() const + { + return static_cast(m_id); + } + + /** + * @brief Check whether this sub-schema has a title + * + * @return boolean value + */ + bool hasTitle() const + { + return static_cast(m_title); + } + + void setAlwaysInvalid(bool value) + { + m_alwaysInvalid = value; + } + + /** + * @brief Set the description for this sub-schema + * + * The description will not be used for validation, but may be used as part + * of the user interface for interacting with schemas and sub-schemas. As + * an example, it may be used as part of the validation error descriptions + * that are produced by the Validator and ValidationVisitor classes. + * + * @param description new description + */ + void setDescription(const std::string &description) + { + m_description = description; + } + + void setId(const std::string &id) + { + m_id = id; + } + + /** + * @brief Set the title for this sub-schema + * + * The title will not be used for validation, but may be used as part + * of the user interface for interacting with schemas and sub-schema. As an + * example, it may be used as part of the validation error descriptions + * that are produced by the Validator and ValidationVisitor classes. + * + * @param title new title + */ + void setTitle(const std::string &title) + { + m_title = title; + } + +protected: + + CustomAlloc m_allocFn; + + CustomFree m_freeFn; + +private: + + bool m_alwaysInvalid; + + /// List of pointers to constraints that apply to this schema. + std::vector m_constraints; + + /// Schema description (optional) + opt::optional m_description; + + /// Id to apply when resolving the schema URI + opt::optional m_id; + + /// Title string associated with the schema (optional) + opt::optional m_title; +}; + +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/file_utils.hpp b/subprojects/valijson/include/valijson/utils/file_utils.hpp new file mode 100644 index 000000000..92757cf64 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/file_utils.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +namespace valijson { +namespace utils { + +/** + * Load a file into a string + * + * @param path path to the file to be loaded + * @param dest string into which file should be loaded + * + * @return true if loaded, false otherwise + */ +inline bool loadFile(const std::string &path, std::string &dest) +{ + // Open file for reading + std::ifstream file(path.c_str()); + if (!file.is_open()) { + return false; + } + + // Allocate space for file contents + file.seekg(0, std::ios::end); + const std::streamoff offset = file.tellg(); + if (offset < 0 || offset > std::numeric_limits::max()) { + return false; + } + + dest.clear(); + dest.reserve(static_cast(offset)); + + // Assign file contents to destination string + file.seekg(0, std::ios::beg); + dest.assign(std::istreambuf_iterator(file), + std::istreambuf_iterator()); + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/json11_utils.hpp b/subprojects/valijson/include/valijson/utils/json11_utils.hpp new file mode 100644 index 000000000..b20b47854 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/json11_utils.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +#include + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, json11::Json &document) +{ + // Load schema JSON from file + std::string file; + if (!loadFile(path, file)) { + std::cerr << "Failed to load json from file '" << path << "'." << std::endl; + return false; + } + + // Parse schema + std::string err; + document = json11::Json::parse(file, err); + if (!err.empty()) { + std::cerr << "json11 failed to parse the document:" << std::endl + << "Parse error: " << err << std::endl; + return false; + } + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/jsoncpp_utils.hpp b/subprojects/valijson/include/valijson/utils/jsoncpp_utils.hpp new file mode 100644 index 000000000..49a43b624 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/jsoncpp_utils.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, Json::Value &document) +{ + // Load schema JSON from file + std::string file; + if (!loadFile(path, file)) { + std::cerr << "Failed to load json from file '" << path << "'." << std::endl; + return false; + } + + const auto fileLength = static_cast(file.length()); + Json::CharReaderBuilder builder; + const std::unique_ptr reader(builder.newCharReader()); + std::string err; + if (!reader->parse(file.c_str(), file.c_str() + fileLength, &document, &err)) { + std::cerr << "Jsoncpp parser failed to parse the document:" << std::endl << err; + return false; + } + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/nlohmann_json_utils.hpp b/subprojects/valijson/include/valijson/utils/nlohmann_json_utils.hpp new file mode 100644 index 000000000..b3cbe205b --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/nlohmann_json_utils.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include +#include +#include + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, nlohmann::json &document) +{ + // Load schema JSON from file + std::string file; + if (!loadFile(path, file)) { + std::cerr << "Failed to load json from file '" << path << "'." + << std::endl; + return false; + } + + // Parse schema +#if VALIJSON_USE_EXCEPTION + try { + document = nlohmann::json::parse(file); + } catch (std::invalid_argument const& exception) { + std::cerr << "nlohmann::json failed to parse the document\n" + << "Parse error:" << exception.what() << "\n"; + return false; + } +#else + document = nlohmann::json::parse(file, nullptr, false); + if (document.is_discarded()) { + std::cerr << "nlohmann::json failed to parse the document."; + return false; + } +#endif + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/picojson_utils.hpp b/subprojects/valijson/include/valijson/utils/picojson_utils.hpp new file mode 100644 index 000000000..36420d2e0 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/picojson_utils.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#ifdef _MSC_VER +#pragma warning(disable: 4706) +#include +#pragma warning(default: 4706) +#else +#include +#endif + +#include + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, picojson::value &document) +{ + // Load schema JSON from file + std::string file; + if (!loadFile(path, file)) { + std::cerr << "Failed to load json from file '" << path << "'." << std::endl; + return false; + } + + // Parse schema + std::string err = picojson::parse(document, file); + if (!err.empty()) { + std::cerr << "PicoJson failed to parse the document:" << std::endl + << "Parse error: " << err << std::endl; + return false; + } + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/poco_json_utils.hpp b/subprojects/valijson/include/valijson/utils/poco_json_utils.hpp new file mode 100644 index 000000000..145cec346 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/poco_json_utils.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, Poco::Dynamic::Var &document) +{ + // Load schema JSON from file + std::string file; + if (!loadFile(path, file)) { + std::cerr << "Failed to load json from file '" << path << "'." + << std::endl; + return false; + } + + // Parse schema + try { + document = Poco::JSON::Parser().parse(file); + } catch (Poco::Exception const& exception) { + std::cerr << "Poco::JSON failed to parse the document\n" + << "Parse error:" << exception.what() << "\n"; + return false; + } + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/property_tree_utils.hpp b/subprojects/valijson/include/valijson/utils/property_tree_utils.hpp new file mode 100644 index 000000000..2317f127c --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/property_tree_utils.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wshadow" +# include +# pragma clang diagnostic pop +#else +# include +#endif + +// Source locations were added in boost 1.73. +#include +#if (BOOST_VERSION > 107300) +#include +#endif + +#include +#include + +#if !VALIJSON_USE_EXCEPTION + +namespace boost { + +// Boost requires used-defined exception throwers when exceptions are +// disabled. +// NOTE: BOOST_NORETURN attribute was added in 1.71. +#if (BOOST_VERSION >= 107100) +BOOST_NORETURN +#endif +void throw_exception(std::exception const & e ) { + valijson::throwRuntimeError(e.what()); +} + +// Source location override was added in 1.73. +#if (BOOST_VERSION >= 107300) +BOOST_NORETURN +void throw_exception(std::exception const & e, boost::source_location const & loc ) { + valijson::throwRuntimeError(e.what()); +} +#endif + +} // namespace boost + +#endif + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, boost::property_tree::ptree &document) +{ +#if !defined(BOOST_NO_EXCEPTIONS) + try { +#endif + boost::property_tree::read_json(path, document); +#if !defined(BOOST_NO_EXCEPTIONS) + } catch (std::exception &e) { + std::cerr << "Boost Property Tree JSON parser failed to parse the document:" << std::endl; + std::cerr << e.what() << std::endl; + return false; + } +#endif + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/qtjson_utils.hpp b/subprojects/valijson/include/valijson/utils/qtjson_utils.hpp new file mode 100644 index 000000000..c05b0f0b6 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/qtjson_utils.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, QJsonValue &root) +{ + // Load schema JSON from file + QFile file(QString::fromStdString(path)); + if (!file.open(QFile::ReadOnly)) { + std::cerr << "Failed to load json from file '" << path << "'." << std::endl; + return false; + } + + QByteArray data = file.readAll(); + + // Parse schema + QJsonParseError parseError; + QJsonDocument doc = QJsonDocument::fromJson(data); + if (doc.isNull()) { + std::cerr << "qt failed to parse the document:" << std::endl + << parseError.errorString().toStdString() << std::endl; + return false; + } else if (doc.isObject()) { + root = QJsonValue(doc.object()); + } else if (doc.isArray()) { + root = QJsonValue(doc.array()); + } else if (doc.isEmpty()) { + root = QJsonValue(); + } + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/rapidjson_utils.hpp b/subprojects/valijson/include/valijson/utils/rapidjson_utils.hpp new file mode 100644 index 000000000..2f90ba933 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/rapidjson_utils.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace valijson { +namespace utils { + +template +inline bool loadDocument(const std::string &path, rapidjson::GenericDocument &document) +{ + // Load schema JSON from file + std::string file; + if (!loadFile(path, file)) { + std::cerr << "Failed to load json from file '" << path << "'." << std::endl; + return false; + } + + // Parse schema +#if VALIJSON_USE_EXCEPTIONS + try { +#endif + document.template Parse(file.c_str()); + if (document.HasParseError()) { + std::cerr << "RapidJson failed to parse the document:" << std::endl; + std::cerr << "Parse error: " << document.GetParseError() << std::endl; + std::cerr << "Near: " << file.substr((std::max)(size_t(0), document.GetErrorOffset() - 20), 40) << std::endl; + return false; + } +#if VALIJSON_USE_EXCEPTIONS + } catch (const std::runtime_error &e) { + std::cerr << "RapidJson failed to parse the document:" << std::endl; + std::cerr << "Runtime error: " << e.what() << std::endl; + return false; + } +#endif + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/utils/utf8_utils.hpp b/subprojects/valijson/include/valijson/utils/utf8_utils.hpp new file mode 100644 index 000000000..f1e01a547 --- /dev/null +++ b/subprojects/valijson/include/valijson/utils/utf8_utils.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +#include + +/* + Basic UTF-8 manipulation routines, adapted from code that was released into + the public domain by Jeff Bezanson. +*/ + +namespace valijson { +namespace utils { + +static const uint32_t offsetsFromUTF8[6] = { + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + +/* is c the start of a utf8 sequence? */ +inline bool isutf(char c) { + return ((c & 0xC0) != 0x80); +} + +/* reads the next utf-8 sequence out of a string, updating an index */ +inline uint64_t u8_nextchar(const char *s, uint64_t *i) +{ + uint64_t ch = 0; + int sz = 0; + + do { + ch <<= 6; + ch += static_cast(s[(*i)++]); + sz++; + } while (s[*i] && !isutf(s[*i])); + ch -= offsetsFromUTF8[sz-1]; + + return ch; +} + +/* number of characters */ +inline uint64_t u8_strlen(const char *s) +{ + constexpr auto maxLength = std::numeric_limits::max(); + uint64_t count = 0; + uint64_t i = 0; + + while (s[i] != 0 && u8_nextchar(s, &i) != 0) { + if (i == maxLength) { + throwRuntimeError( + "String exceeded maximum size of " + + std::to_string(maxLength) + " bytes."); + } + count++; + } + + return count; +} + +} // namespace utils +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/validation_results.hpp b/subprojects/valijson/include/valijson/validation_results.hpp new file mode 100644 index 000000000..b82131793 --- /dev/null +++ b/subprojects/valijson/include/valijson/validation_results.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include + +namespace valijson { + +/** + * @brief Class that encapsulates the storage of validation errors. + * + * This class maintains an internal FIFO queue of errors that are reported + * during validation. Errors are pushed on to the back of an internal + * queue, and can retrieved by popping them from the front of the queue. + */ +class ValidationResults +{ +public: + + /** + * @brief Describes a validation error. + * + * This struct is used to pass around the context and description of a + * validation error. + */ + struct Error + { + /// Path to the node that failed validation. + std::vector context; + + /// A detailed description of the validation error. + std::string description; + }; + + /** + * @brief Return begin iterator for results in the queue. + */ + std::deque::const_iterator begin() const + { + return m_errors.begin(); + } + + /** + * @brief Return end iterator for results in the queue. + */ + std::deque::const_iterator end() const + { + return m_errors.end(); + } + + /** + * @brief Return the number of errors in the queue. + */ + size_t numErrors() const + { + return m_errors.size(); + } + + /** + * @brief Copy an Error and push it on to the back of the queue. + * + * @param error Reference to an Error object to be copied. + */ + void pushError(const Error &error) + { + m_errors.push_back(error); + } + + /** + * @brief Push an error onto the back of the queue. + * + * @param context Context of the validation error. + * @param description Description of the validation error. + */ + void + pushError(const std::vector &context, const std::string &description) + { + m_errors.push_back({context, description}); + } + + /** + * @brief Pop an error from the front of the queue. + * + * @param error Reference to an Error object to populate. + * + * @returns true if an Error was popped, false otherwise. + */ + bool + popError(Error &error) + { + if (m_errors.empty()) { + return false; + } + + error = m_errors.front(); + m_errors.pop_front(); + return true; + } + +private: + + /// FIFO queue of validation errors that have been reported + std::deque m_errors; +}; + +} // namespace valijson diff --git a/subprojects/valijson/include/valijson/validation_visitor.hpp b/subprojects/valijson/include/valijson/validation_visitor.hpp new file mode 100644 index 000000000..cd4442d7c --- /dev/null +++ b/subprojects/valijson/include/valijson/validation_visitor.hpp @@ -0,0 +1,1792 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4702 ) +#endif + +namespace valijson { + +class ValidationResults; + +/** + * @brief Implementation of the ConstraintVisitor interface that validates a + * target document + * + * @tparam AdapterType Adapter type for the target document. + */ +template +class ValidationVisitor: public constraints::ConstraintVisitor +{ +public: + + /** + * @brief Construct a new validator for a given target value and context. + * + * @param target Target value to be validated + * @param context Current context for validation error descriptions, + * only used if results is set. + * @param strictTypes Use strict type comparison + * @param results Optional pointer to ValidationResults object, for + * recording error descriptions. If this pointer is set + * to nullptr, validation errors will caused validation to + * stop immediately. + * @param regexesCache Cache of already created std::regex objects for pattern + * constraints. + */ + ValidationVisitor(const AdapterType &target, + std::vector context, + const bool strictTypes, + ValidationResults *results, + std::unordered_map& regexesCache) + : m_target(target), + m_context(std::move(context)), + m_results(results), + m_strictTypes(strictTypes), + m_regexesCache(regexesCache) { } + + /** + * @brief Validate the target against a schema. + * + * When a ValidationResults object has been set via the 'results' member + * variable, validation will proceed as long as no fatal errors occur, + * with error descriptions added to the ValidationResults object. + * + * If a pointer to a ValidationResults instance is not provided, validation + * will only continue for as long as the constraints are validated + * successfully. + * + * @param subschema Sub-schema that the target must validate against + * + * @return \c true if validation passes; \c false otherwise + */ + bool validateSchema(const Subschema &subschema) + { + if (subschema.getAlwaysInvalid()) { + return false; + } + + // Wrap the validationCallback() function below so that it will be + // passed a reference to a constraint (_1), and a reference to the + // visitor (*this). + Subschema::ApplyFunction fn(std::bind(validationCallback, std::placeholders::_1, std::ref(*this))); + + // Perform validation against each constraint defined in the schema + if (m_results == nullptr) { + // The applyStrict() function will return immediately if the + // callback function returns false + if (!subschema.applyStrict(fn)) { + return false; + } + } else { + // The apply() function will iterate over all constraints in the + // schema, even if the callback function returns false. Once + // iteration is complete, the apply() function will return true + // only if all invokations of the callback function returned true. + if (!subschema.apply(fn)) { + return false; + } + } + + return true; + } + + /** + * @brief Validate a value against an AllOfConstraint + * + * An allOf constraint provides a set of child schemas against which the + * target must be validated in order for the constraint to the satifisfied. + * + * When a ValidationResults object has been set via the 'results' member + * variable, validation will proceed as long as no fatal errors occur, + * with error descriptions added to the ValidationResults object. + * + * If a pointer to a ValidationResults instance is not provided, validation + * will only continue for as long as the child schemas are validated + * successfully. + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if validation passes; \c false otherwise + */ + bool visit(const AllOfConstraint &constraint) override + { + bool validated = true; + constraint.applyToSubschemas( + ValidateSubschemas(m_target, m_context, true, false, *this, m_results, nullptr, &validated)); + + return validated; + } + + /** + * @brief Validate a value against an AnyOfConstraint + * + * An anyOf constraint provides a set of child schemas, any of which the + * target may be validated against in order for the constraint to the + * satifisfied. + * + * Because an anyOf constraint does not require the target to validate + * against all child schemas, if validation against a single schema fails, + * the results will not be added to a ValidationResults object. Only if + * validation fails for all child schemas will an error be added to the + * ValidationResults object. + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if validation passes; \c false otherwise + */ + bool visit(const AnyOfConstraint &constraint) override + { + unsigned int numValidated = 0; + + ValidationResults newResults; + ValidationResults *childResults = (m_results) ? &newResults : nullptr; + + ValidationVisitor v(m_target, m_context, m_strictTypes, childResults, m_regexesCache); + constraint.applyToSubschemas( + ValidateSubschemas(m_target, m_context, false, true, v, childResults, &numValidated, nullptr)); + + if (numValidated == 0 && m_results) { + ValidationResults::Error childError; + while (childResults->popError(childError)) { + m_results->pushError( childError.context, childError.description); + } + m_results->pushError(m_context, "Failed to validate against any schemas allowed by anyOf constraint."); + } + + return numValidated > 0; + } + + /** + * @brief Validate current node using a set of 'if', 'then' and 'else' subschemas + * + * A conditional constraint allows a document to be validated against one of two additional + * subschemas (specified via 'then' or 'else' properties) depending on whether the document + * satifies an optional subschema (specified via the 'if' property). + * + * @param constraint ConditionalConstraint that the current node must validate against + * + * @return \c true if validation passes; \c false otherwise + */ + bool visit(const ConditionalConstraint &constraint) override + { + ValidationResults newResults; + ValidationResults* conditionalResults = (m_results) ? &newResults : nullptr; + + // Create a validator to evaluate the conditional + ValidationVisitor ifValidator(m_target, m_context, m_strictTypes, nullptr, m_regexesCache); + ValidationVisitor thenElseValidator(m_target, m_context, m_strictTypes, conditionalResults, m_regexesCache); + + bool validated = false; + if (ifValidator.validateSchema(*constraint.getIfSubschema())) { + const Subschema *thenSubschema = constraint.getThenSubschema(); + validated = thenSubschema == nullptr || thenElseValidator.validateSchema(*thenSubschema); + } else { + const Subschema *elseSubschema = constraint.getElseSubschema(); + validated = elseSubschema == nullptr || thenElseValidator.validateSchema(*elseSubschema); + } + + if (!validated && m_results) { + ValidationResults::Error conditionalError; + while (conditionalResults->popError(conditionalError)) { + m_results->pushError(conditionalError.context, conditionalError.description); + } + m_results->pushError(m_context, "Failed to validate against a conditional schema set by if-then-else constraints."); + } + + return validated; + } + + /** + * @brief Validate current node using a 'const' constraint + * + * A const constraint allows a document to be validated against a specific value. + * + * @param constraint ConstConstraint that the current node must validate against + * + * @return \c true if validation passes; \f false otherwise + */ + bool visit(const ConstConstraint &constraint) override + { + if (!constraint.getValue()->equalTo(m_target, m_strictTypes)) { + if (m_results) { + m_results->pushError(m_context, "Failed to match expected value set by 'const' constraint."); + } + return false; + } + + return true; + } + + /** + * @brief Validate current node using a 'contains' constraint + * + * A contains constraint is satisfied if the target is not an array, or if it is an array, + * only if it contains at least one value that matches the specified schema. + * + * @param constraint ContainsConstraint that the current node must validate against + * + * @return \c true if validation passes; \c false otherwise + */ + bool visit(const ContainsConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isArray()) || !m_target.maybeArray()) { + return true; + } + + const Subschema *subschema = constraint.getSubschema(); + const typename AdapterType::Array arr = m_target.asArray(); + + bool validated = false; + for (const auto &el : arr) { + ValidationVisitor containsValidator(el, m_context, m_strictTypes, nullptr, m_regexesCache); + if (containsValidator.validateSchema(*subschema)) { + validated = true; + break; + } + } + + if (!validated) { + if (m_results) { + m_results->pushError(m_context, "Failed to any values against subschema in 'contains' constraint."); + } + + return false; + } + + return validated; + } + + /** + * @brief Validate current node against a 'dependencies' constraint + * + * A 'dependencies' constraint can be used to specify property-based or + * schema-based dependencies that must be fulfilled when a particular + * property is present in an object. + * + * Property-based dependencies define a set of properties that must be + * present in addition to a particular property, whereas a schema-based + * dependency defines an additional schema that the current document must + * validate against. + * + * @param constraint DependenciesConstraint that the current node + * must validate against + * + * @return \c true if validation passes; \c false otherwise + */ + bool visit(const DependenciesConstraint &constraint) override + { + // Ignore non-objects + if ((m_strictTypes && !m_target.isObject()) || (!m_target.maybeObject())) { + return true; + } + + // Object to be validated + const typename AdapterType::Object object = m_target.asObject(); + + // Cleared if validation fails + bool validated = true; + + // Iterate over all dependent properties defined by this constraint, + // invoking the DependentPropertyValidator functor once for each + // set of dependent properties + constraint.applyToPropertyDependencies(ValidatePropertyDependencies(object, m_context, m_results, &validated)); + if (!m_results && !validated) { + return false; + } + + // Iterate over all dependent schemas defined by this constraint, + // invoking the DependentSchemaValidator function once for each schema + // that must be validated if a given property is present + constraint.applyToSchemaDependencies(ValidateSchemaDependencies( + object, m_context, *this, m_results, &validated)); + if (!m_results && !validated) { + return false; + } + + return validated; + } + + /** + * @brief Validate current node against an EnumConstraint + * + * Validation succeeds if the target is equal to one of the values provided + * by the EnumConstraint. + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if validation succeeds; \c false otherwise + */ + bool visit(const EnumConstraint &constraint) override + { + unsigned int numValidated = 0; + constraint.applyToValues( + ValidateEquality(m_target, m_context, false, true, m_strictTypes, nullptr, &numValidated)); + + if (numValidated == 0) { + if (m_results) { + m_results->pushError(m_context, "Failed to match against any enum values."); + } + + return false; + } + + return numValidated > 0; + } + + /** + * @brief Validate a value against a LinearItemsConstraint + * + * A LinearItemsConstraint represents an 'items' constraint that specifies, + * for each item in array, an individual sub-schema that the item must + * validate against. The LinearItemsConstraint class also captures the + * presence of an 'additionalItems' constraint, which specifies a default + * sub-schema that should be used if an array contains more items than + * there are sub-schemas in the 'items' constraint. + * + * If the current value is not an array, validation always succeeds. + * + * @param constraint SingularItemsConstraint to validate against + * + * @returns \c true if validation is successful; \c false otherwise + */ + bool visit(const LinearItemsConstraint &constraint) override + { + // Ignore values that are not arrays + if ((m_strictTypes && !m_target.isArray()) || (!m_target.maybeArray())) { + return true; + } + + // Sub-schema to validate against when number of items in array exceeds + // the number of sub-schemas provided by the 'items' constraint + const Subschema * const additionalItemsSubschema = constraint.getAdditionalItemsSubschema(); + + // Track how many items are validated using 'items' constraint + unsigned int numValidated = 0; + + // Array to validate + const typename AdapterType::Array arr = m_target.asArray(); + const size_t arrSize = arr.size(); + + // Track validation status + bool validated = true; + + // Validate as many items as possible using 'items' sub-schemas + const size_t itemSubschemaCount = constraint.getItemSubschemaCount(); + if (itemSubschemaCount > 0) { + if (!additionalItemsSubschema) { + if (arrSize > itemSubschemaCount) { + if (!m_results) { + return false; + } + m_results->pushError(m_context, "Array contains more items than allowed by items constraint."); + validated = false; + } + } + + constraint.applyToItemSubschemas( + ValidateItems(arr, m_context, true, m_results != nullptr, m_strictTypes, m_results, &numValidated, + &validated, m_regexesCache)); + + if (!m_results && !validated) { + return false; + } + } + + // Validate remaining items using 'additionalItems' sub-schema + if (numValidated < arrSize) { + if (additionalItemsSubschema) { + // Begin validation from the first item not validated against + // an sub-schema provided by the 'items' constraint + unsigned int index = numValidated; + typename AdapterType::Array::const_iterator begin = arr.begin(); + begin.advance(numValidated); + for (typename AdapterType::Array::const_iterator itr = begin; + itr != arr.end(); ++itr) { + + // Update context for current array item + std::vector newContext = m_context; + newContext.push_back("[" + std::to_string(index) + "]"); + + ValidationVisitor validator(*itr, newContext, m_strictTypes, m_results, m_regexesCache); + + if (!validator.validateSchema(*additionalItemsSubschema)) { + if (m_results) { + m_results->pushError(m_context, "Failed to validate item #" + std::to_string(index) + + " against additional items schema."); + validated = false; + } else { + return false; + } + } + + index++; + } + + } else if (m_results) { + m_results->pushError(m_context, "Cannot validate item #" + std::to_string(numValidated) + + " or greater using 'items' constraint or 'additionalItems' constraint."); + validated = false; + + } else { + return false; + } + } + + return validated; + } + + /** + * @brief Validate a value against a MaximumConstraint object + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if constraints are satisfied; \c false otherwise + */ + bool visit(const MaximumConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isNumber()) || !m_target.maybeDouble()) { + // Ignore values that are not numbers + return true; + } + + const double maximum = constraint.getMaximum(); + + if (constraint.getExclusiveMaximum()) { + if (m_target.asDouble() >= maximum) { + if (m_results) { + m_results->pushError(m_context, "Expected number less than " + std::to_string(maximum)); + } + + return false; + } + + } else if (m_target.asDouble() > maximum) { + if (m_results) { + m_results->pushError(m_context, "Expected number less than or equal to " + std::to_string(maximum)); + } + + return false; + } + + return true; + } + + /** + * @brief Validate a value against a MaxItemsConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if constraint is satisfied; \c false otherwise + */ + bool visit(const MaxItemsConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isArray()) || !m_target.maybeArray()) { + return true; + } + + const uint64_t maxItems = constraint.getMaxItems(); + if (m_target.asArray().size() <= maxItems) { + return true; + } + + if (m_results) { + m_results->pushError(m_context, "Array should contain no more than " + std::to_string(maxItems) + + " elements."); + } + + return false; + } + + /** + * @brief Validate a value against a MaxLengthConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if constraint is satisfied; \c false otherwise + */ + bool visit(const MaxLengthConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isString()) || !m_target.maybeString()) { + return true; + } + + const std::string s = m_target.asString(); + const uint64_t len = utils::u8_strlen(s.c_str()); + const uint64_t maxLength = constraint.getMaxLength(); + if (len <= maxLength) { + return true; + } + + if (m_results) { + m_results->pushError(m_context, "String should be no more than " + std::to_string(maxLength) + + " characters in length."); + } + + return false; + } + + /** + * @brief Validate a value against a MaxPropertiesConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const MaxPropertiesConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isObject()) || !m_target.maybeObject()) { + return true; + } + + const uint64_t maxProperties = constraint.getMaxProperties(); + + if (m_target.asObject().size() <= maxProperties) { + return true; + } + + if (m_results) { + m_results->pushError(m_context, "Object should have no more than " + std::to_string(maxProperties) + + " properties."); + } + + return false; + } + + /** + * @brief Validate a value against a MinimumConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const MinimumConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isNumber()) || !m_target.maybeDouble()) { + // Ignore values that are not numbers + return true; + } + + const double minimum = constraint.getMinimum(); + + if (constraint.getExclusiveMinimum()) { + if (m_target.asDouble() <= minimum) { + if (m_results) { + m_results->pushError(m_context, "Expected number greater than " + std::to_string(minimum)); + } + + return false; + } + } else if (m_target.asDouble() < minimum) { + if (m_results) { + m_results->pushError(m_context, "Expected number greater than or equal to " + std::to_string(minimum)); + } + + return false; + } + + return true; + } + + /** + * @brief Validate a value against a MinItemsConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const MinItemsConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isArray()) || !m_target.maybeArray()) { + return true; + } + + const uint64_t minItems = constraint.getMinItems(); + if (m_target.asArray().size() >= minItems) { + return true; + } + + if (m_results) { + m_results->pushError(m_context, "Array should contain no fewer than " + std::to_string(minItems) + + " elements."); + } + + return false; + } + + /** + * @brief Validate a value against a MinLengthConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const MinLengthConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isString()) || !m_target.maybeString()) { + return true; + } + + const std::string s = m_target.asString(); + const uint64_t len = utils::u8_strlen(s.c_str()); + const uint64_t minLength = constraint.getMinLength(); + if (len >= minLength) { + return true; + } + + if (m_results) { + m_results->pushError(m_context, "String should be no fewer than " + std::to_string(minLength) + + " characters in length."); + } + + return false; + } + + /** + * @brief Validate a value against a MinPropertiesConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const MinPropertiesConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isObject()) || !m_target.maybeObject()) { + return true; + } + + const uint64_t minProperties = constraint.getMinProperties(); + + if (m_target.asObject().size() >= minProperties) { + return true; + } + + if (m_results) { + m_results->pushError(m_context, "Object should have no fewer than " + std::to_string(minProperties) + + " properties."); + } + + return false; + } + + /** + * @brief Validate a value against a MultipleOfDoubleConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const MultipleOfDoubleConstraint &constraint) override + { + const double divisor = constraint.getDivisor(); + + double d = 0.; + if (m_target.maybeDouble()) { + if (!m_target.asDouble(d)) { + if (m_results) { + m_results->pushError(m_context, "Value could not be converted " + "to a number to check if it is a multiple of " + std::to_string(divisor)); + } + return false; + } + } else if (m_target.maybeInteger()) { + int64_t i = 0; + if (!m_target.asInteger(i)) { + if (m_results) { + m_results->pushError(m_context, "Value could not be converted " + "to a number to check if it is a multiple of " + std::to_string(divisor)); + } + return false; + } + d = static_cast(i); + } else { + return true; + } + + if (d == 0) { + return true; + } + + const double r = remainder(d, divisor); + + if (fabs(r) > std::numeric_limits::epsilon()) { + if (m_results) { + m_results->pushError(m_context, "Value should be a multiple of " + std::to_string(divisor)); + } + return false; + } + + return true; + } + + /** + * @brief Validate a value against a MultipleOfIntConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const MultipleOfIntConstraint &constraint) override + { + const int64_t divisor = constraint.getDivisor(); + + int64_t i = 0; + if (m_target.maybeInteger()) { + if (!m_target.asInteger(i)) { + if (m_results) { + m_results->pushError(m_context, "Value could not be converted to an integer for multipleOf check"); + } + return false; + } + } else if (m_target.maybeDouble()) { + double d; + if (!m_target.asDouble(d)) { + if (m_results) { + m_results->pushError(m_context, "Value could not be converted to a double for multipleOf check"); + } + return false; + } + i = static_cast(d); + } else { + return true; + } + + if (i == 0) { + return true; + } + + if (i % divisor != 0) { + if (m_results) { + m_results->pushError(m_context, "Value should be a multiple of " + std::to_string(divisor)); + } + return false; + } + + return true; + } + + /** + * @brief Validate a value against a NotConstraint + * + * If the subschema NotConstraint currently holds a nullptr, the + * schema will be treated like the empty schema. Therefore validation + * will always fail. + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const NotConstraint &constraint) override + { + const Subschema *subschema = constraint.getSubschema(); + if (!subschema) { + // Treat nullptr like empty schema + return false; + } + + ValidationVisitor v(m_target, m_context, m_strictTypes, nullptr, m_regexesCache); + if (v.validateSchema(*subschema)) { + if (m_results) { + m_results->pushError(m_context, + "Target should not validate against schema specified in 'not' constraint."); + } + + return false; + } + + return true; + } + + /** + * @brief Validate a value against a OneOfConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const OneOfConstraint &constraint) override + { + unsigned int numValidated = 0; + + ValidationResults newResults; + ValidationResults *childResults = (m_results) ? &newResults : nullptr; + + ValidationVisitor v(m_target, m_context, m_strictTypes, childResults, m_regexesCache); + constraint.applyToSubschemas( + ValidateSubschemas(m_target, m_context, true, true, v, childResults, &numValidated, nullptr)); + + if (numValidated == 0) { + if (m_results) { + ValidationResults::Error childError; + while (childResults->popError(childError)) { + m_results->pushError( + childError.context, + childError.description); + } + m_results->pushError(m_context, "Failed to validate against any " + "child schemas allowed by oneOf constraint."); + } + return false; + } else if (numValidated != 1) { + if (m_results) { + m_results->pushError(m_context, "Failed to validate against exactly one child schema."); + } + return false; + } + + return true; + } + + /** + * @brief Validate a value against a PatternConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const PatternConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isString()) || !m_target.maybeString()) { + return true; + } + + std::string pattern(constraint.getPattern()); + auto it = m_regexesCache.find(pattern); + if (it == m_regexesCache.end()) { + it = m_regexesCache.emplace(pattern, std::regex(pattern)).first; + } + + if (!std::regex_search(m_target.asString(), it->second)) { + if (m_results) { + m_results->pushError(m_context, "Failed to match regex specified by 'pattern' constraint."); + } + + return false; + } + + return true; + } + + /** + * @brief Validate a value against a PatternConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const constraints::PolyConstraint &constraint) override + { + return constraint.validate(m_target, m_context, m_results); + } + + /** + * @brief Validate a value against a PropertiesConstraint + * + * Validation of an object against a PropertiesConstraint proceeds in three + * stages. The first stage finds all properties in the object that have a + * corresponding subschema in the constraint, and validates those properties + * recursively. + * + * Next, the object's properties will be validated against the subschemas + * for any 'patternProperties' that match a given property name. A property + * is required to validate against the sub-schema for all patterns that it + * matches. + * + * Finally, any properties that have not yet been validated against at least + * one subschema will be validated against the 'additionalItems' subschema. + * If this subschema is not present, then all properties must have been + * validated at least once. + * + * Non-object values are always considered valid. + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if the constraint is satisfied; \c false otherwise + */ + bool visit(const PropertiesConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isObject()) || !m_target.maybeObject()) { + return true; + } + + bool validated = true; + + // Track which properties have already been validated + std::set propertiesMatched; + + // Validate properties against subschemas for matching 'properties' + // constraints + const typename AdapterType::Object object = m_target.asObject(); + constraint.applyToProperties( + ValidatePropertySubschemas( + object, m_context, true, m_results != nullptr, true, m_strictTypes, m_results, + &propertiesMatched, &validated, m_regexesCache)); + + // Exit early if validation failed, and we're not collecting exhaustive + // validation results + if (!validated && !m_results) { + return false; + } + + // Validate properties against subschemas for matching patternProperties + // constraints + constraint.applyToPatternProperties( + ValidatePatternPropertySubschemas( + object, m_context, true, false, true, m_strictTypes, m_results, &propertiesMatched, + &validated, m_regexesCache)); + + // Validate against additionalProperties subschema for any properties + // that have not yet been matched + const Subschema *additionalPropertiesSubschema = + constraint.getAdditionalPropertiesSubschema(); + if (!additionalPropertiesSubschema) { + if (propertiesMatched.size() != m_target.getObjectSize()) { + if (m_results) { + std::string unwanted; + for (const typename AdapterType::ObjectMember m : object) { + if (propertiesMatched.find(m.first) == propertiesMatched.end()) { + unwanted = m.first; + break; + } + } + m_results->pushError(m_context, "Object contains a property " + "that could not be validated using 'properties' " + "or 'additionalProperties' constraints: '" + unwanted + "'."); + } + + return false; + } + + return validated; + } + + for (const typename AdapterType::ObjectMember m : object) { + if (propertiesMatched.find(m.first) == propertiesMatched.end()) { + // Update context + std::vector newContext = m_context; + newContext.push_back("[" + m.first + "]"); + + // Create a validator to validate the property's value + ValidationVisitor validator(m.second, newContext, m_strictTypes, m_results, m_regexesCache); + if (!validator.validateSchema(*additionalPropertiesSubschema)) { + if (m_results) { + m_results->pushError(m_context, "Failed to validate against additional properties schema"); + } + + validated = false; + } + } + } + + return validated; + } + + /** + * @brief Validate a value against a PropertyNamesConstraint + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if validation succeeds; \c false otherwise + */ + bool visit(const PropertyNamesConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isObject()) || !m_target.maybeObject()) { + return true; + } + + for (const typename AdapterType::ObjectMember m : m_target.asObject()) { + adapters::StdStringAdapter stringAdapter(m.first); + ValidationVisitor validator(stringAdapter, m_context, m_strictTypes, nullptr, m_regexesCache); + if (!validator.validateSchema(*constraint.getSubschema())) { + return false; + } + } + + return true; + } + + /** + * @brief Validate a value against a RequiredConstraint + * + * A required constraint specifies a list of properties that must be present + * in the target. + * + * @param constraint Constraint that the target must validate against + * + * @return \c true if validation succeeds; \c false otherwise + */ + bool visit(const RequiredConstraint &constraint) override + { + if ((m_strictTypes && !m_target.isObject()) || !m_target.maybeObject()) { + return true; + } + + bool validated = true; + const typename AdapterType::Object object = m_target.asObject(); + constraint.applyToRequiredProperties( + ValidateProperties(object, m_context, true, m_results != nullptr, m_results, &validated)); + + return validated; + } + + /** + * @brief Validate a value against a SingularItemsConstraint + * + * A SingularItemsConstraint represents an 'items' constraint that specifies + * a sub-schema against which all items in an array must validate. If the + * current value is not an array, validation always succeeds. + * + * @param constraint SingularItemsConstraint to validate against + * + * @returns \c true if validation is successful; \c false otherwise + */ + bool visit(const SingularItemsConstraint &constraint) override + { + // Ignore values that are not arrays + if (!m_target.isArray()) { + return true; + } + + // Schema against which all items must validate + const Subschema *itemsSubschema = constraint.getItemsSubschema(); + + // Default items sub-schema accepts all values + if (!itemsSubschema) { + return true; + } + + // Track whether validation has failed + bool validated = true; + + unsigned int index = 0; + for (const AdapterType &item : m_target.getArray()) { + // Update context for current array item + std::vector newContext = m_context; + newContext.push_back("[" + std::to_string(index) + "]"); + + // Create a validator for the current array item + ValidationVisitor validationVisitor(item, newContext, m_strictTypes, m_results, m_regexesCache); + + // Perform validation + if (!validationVisitor.validateSchema(*itemsSubschema)) { + if (m_results) { + m_results->pushError(m_context, "Failed to validate item #" + std::to_string(index) + " in array."); + validated = false; + } else { + return false; + } + } + + index++; + } + + return validated; + } + + /** + * @brief Validate a value against a TypeConstraint + * + * Checks that the target is one of the valid named types, or matches one + * of a set of valid sub-schemas. + * + * @param constraint TypeConstraint to validate against + * + * @return \c true if validation is successful; \c false otherwise + */ + bool visit(const TypeConstraint &constraint) override + { + // Check named types + { + // ValidateNamedTypes functor assumes target is invalid + bool validated = false; + constraint.applyToNamedTypes(ValidateNamedTypes(m_target, false, true, m_strictTypes, &validated)); + if (validated) { + return true; + } + } + + // Check schema-based types + { + unsigned int numValidated = 0; + constraint.applyToSchemaTypes( + ValidateSubschemas(m_target, m_context, false, true, *this, nullptr, &numValidated, nullptr)); + if (numValidated > 0) { + return true; + } else if (m_results) { + m_results->pushError(m_context, "Value type not permitted by 'type' constraint."); + } + } + + return false; + } + + /** + * @brief Validate the uniqueItems constraint represented by a + * UniqueItems object. + * + * A uniqueItems constraint requires that each of the values in an array + * are unique. Comparison is performed recursively. + * + * @param constraint Constraint that the target must validate against + * + * @return true if validation succeeds, false otherwise + */ + bool visit(const UniqueItemsConstraint &) override + { + if ((m_strictTypes && !m_target.isArray()) || !m_target.maybeArray()) { + return true; + } + + // Empty arrays are always valid + if (m_target.getArraySize() == 0) { + return true; + } + + bool validated = true; + + const typename AdapterType::Array targetArray = m_target.asArray(); + const typename AdapterType::Array::const_iterator end = targetArray.end(); + const typename AdapterType::Array::const_iterator secondLast = --targetArray.end(); + unsigned int outerIndex = 0; + typename AdapterType::Array::const_iterator outerItr = targetArray.begin(); + for (; outerItr != secondLast; ++outerItr) { + unsigned int innerIndex = outerIndex + 1; + typename AdapterType::Array::const_iterator innerItr(outerItr); + for (++innerItr; innerItr != end; ++innerItr) { + if (outerItr->equalTo(*innerItr, true)) { + if (!m_results) { + return false; + } + m_results->pushError(m_context, "Elements at indexes #" + std::to_string(outerIndex) + + " and #" + std::to_string(innerIndex) + " violate uniqueness constraint."); + validated = false; + } + ++innerIndex; + } + ++outerIndex; + } + + return validated; + } + +private: + + /** + * @brief Functor to compare a node with a collection of values + */ + struct ValidateEquality + { + ValidateEquality( + const AdapterType &target, + const std::vector &context, + bool continueOnSuccess, + bool continueOnFailure, + bool strictTypes, + ValidationResults *results, + unsigned int *numValidated) + : m_target(target), + m_context(context), + m_continueOnSuccess(continueOnSuccess), + m_continueOnFailure(continueOnFailure), + m_strictTypes(strictTypes), + m_results(results), + m_numValidated(numValidated) { } + + template + bool operator()(const OtherValue &value) const + { + if (value.equalTo(m_target, m_strictTypes)) { + if (m_numValidated) { + (*m_numValidated)++; + } + + return m_continueOnSuccess; + } + + if (m_results) { + m_results->pushError(m_context, "Target value and comparison value are not equal"); + } + + return m_continueOnFailure; + } + + private: + const AdapterType &m_target; + const std::vector &m_context; + bool m_continueOnSuccess; + bool m_continueOnFailure; + bool m_strictTypes; + ValidationResults * const m_results; + unsigned int * const m_numValidated; + }; + + /** + * @brief Functor to validate the presence of a set of properties + */ + struct ValidateProperties + { + ValidateProperties( + const typename AdapterType::Object &object, + const std::vector &context, + bool continueOnSuccess, + bool continueOnFailure, + ValidationResults *results, + bool *validated) + : m_object(object), + m_context(context), + m_continueOnSuccess(continueOnSuccess), + m_continueOnFailure(continueOnFailure), + m_results(results), + m_validated(validated) { } + + template + bool operator()(const StringType &property) const + { + if (m_object.find(property.c_str()) == m_object.end()) { + if (m_validated) { + *m_validated = false; + } + + if (m_results) { + m_results->pushError(m_context, "Missing required property '" + + std::string(property.c_str()) + "'."); + } + + return m_continueOnFailure; + } + + return m_continueOnSuccess; + } + + private: + const typename AdapterType::Object &m_object; + const std::vector &m_context; + bool m_continueOnSuccess; + bool m_continueOnFailure; + ValidationResults * const m_results; + bool * const m_validated; + }; + + /** + * @brief Functor to validate property-based dependencies + */ + struct ValidatePropertyDependencies + { + ValidatePropertyDependencies( + const typename AdapterType::Object &object, + const std::vector &context, + ValidationResults *results, + bool *validated) + : m_object(object), + m_context(context), + m_results(results), + m_validated(validated) { } + + template + bool operator()(const StringType &propertyName, const ContainerType &dependencyNames) const + { + const std::string propertyNameKey(propertyName.c_str()); + if (m_object.find(propertyNameKey) == m_object.end()) { + return true; + } + + typedef typename ContainerType::value_type ValueType; + for (const ValueType &dependencyName : dependencyNames) { + const std::string dependencyNameKey(dependencyName.c_str()); + if (m_object.find(dependencyNameKey) == m_object.end()) { + if (m_validated) { + *m_validated = false; + } + if (m_results) { + m_results->pushError(m_context, "Missing dependency '" + dependencyNameKey + "'."); + } else { + return false; + } + } + } + + return true; + } + + private: + const typename AdapterType::Object &m_object; + const std::vector &m_context; + ValidationResults * const m_results; + bool * const m_validated; + }; + + /** + * @brief Functor to validate against sub-schemas in 'items' constraint + */ + struct ValidateItems + { + ValidateItems( + const typename AdapterType::Array &arr, + const std::vector &context, + bool continueOnSuccess, + bool continueOnFailure, + bool strictTypes, + ValidationResults *results, + unsigned int *numValidated, + bool *validated, + std::unordered_map& regexesCache) + : m_arr(arr), + m_context(context), + m_continueOnSuccess(continueOnSuccess), + m_continueOnFailure(continueOnFailure), + m_strictTypes(strictTypes), + m_results(results), + m_numValidated(numValidated), + m_validated(validated), + m_regexesCache(regexesCache) { } + + bool operator()(unsigned int index, const Subschema *subschema) const + { + // Check that there are more elements to validate + if (index >= m_arr.size()) { + return false; + } + + // Update context + std::vector newContext = m_context; + newContext.push_back("[" + std::to_string(index) + "]"); + + // Find array item + typename AdapterType::Array::const_iterator itr = m_arr.begin(); + itr.advance(index); + + // Validate current array item + ValidationVisitor validator(*itr, newContext, m_strictTypes, m_results, m_regexesCache); + if (validator.validateSchema(*subschema)) { + if (m_numValidated) { + (*m_numValidated)++; + } + + return m_continueOnSuccess; + } + + if (m_validated) { + *m_validated = false; + } + + if (m_results) { + m_results->pushError(newContext, "Failed to validate item #" + std::to_string(index) + + " against corresponding item schema."); + } + + return m_continueOnFailure; + } + + private: + const typename AdapterType::Array &m_arr; + const std::vector &m_context; + bool m_continueOnSuccess; + bool m_continueOnFailure; + bool m_strictTypes; + ValidationResults * const m_results; + unsigned int * const m_numValidated; + bool * const m_validated; + std::unordered_map& m_regexesCache; + }; + + /** + * @brief Functor to validate value against named JSON types + */ + struct ValidateNamedTypes + { + ValidateNamedTypes( + const AdapterType &target, + bool continueOnSuccess, + bool continueOnFailure, + bool strictTypes, + bool *validated) + : m_target(target), + m_continueOnSuccess(continueOnSuccess), + m_continueOnFailure(continueOnFailure), + m_strictTypes(strictTypes), + m_validated(validated) { } + + bool operator()(constraints::TypeConstraint::JsonType jsonType) const + { + typedef constraints::TypeConstraint TypeConstraint; + + bool valid = false; + + switch (jsonType) { + case TypeConstraint::kAny: + valid = true; + break; + case TypeConstraint::kArray: + valid = m_target.isArray(); + break; + case TypeConstraint::kBoolean: + valid = m_target.isBool() || (!m_strictTypes && m_target.maybeBool()); + break; + case TypeConstraint::kInteger: + valid = m_target.isInteger() || (!m_strictTypes && m_target.maybeInteger()); + break; + case TypeConstraint::kNull: + valid = m_target.isNull() || (!m_strictTypes && m_target.maybeNull()); + break; + case TypeConstraint::kNumber: + valid = m_target.isNumber() || (!m_strictTypes && m_target.maybeDouble()); + break; + case TypeConstraint::kObject: + valid = m_target.isObject(); + break; + case TypeConstraint::kString: + valid = m_target.isString(); + break; + default: + break; + } + + if (valid && m_validated) { + *m_validated = true; + } + + return (valid && m_continueOnSuccess) || m_continueOnFailure; + } + + private: + const AdapterType m_target; + const bool m_continueOnSuccess; + const bool m_continueOnFailure; + const bool m_strictTypes; + bool * const m_validated; + }; + + /** + * @brief Functor to validate object properties against sub-schemas + * defined by a 'patternProperties' constraint + */ + struct ValidatePatternPropertySubschemas + { + ValidatePatternPropertySubschemas( + const typename AdapterType::Object &object, + const std::vector &context, + bool continueOnSuccess, + bool continueOnFailure, + bool continueIfUnmatched, + bool strictTypes, + ValidationResults *results, + std::set *propertiesMatched, + bool *validated, + std::unordered_map& regexesCache) + : m_object(object), + m_context(context), + m_continueOnSuccess(continueOnSuccess), + m_continueOnFailure(continueOnFailure), + m_continueIfUnmatched(continueIfUnmatched), + m_strictTypes(strictTypes), + m_results(results), + m_propertiesMatched(propertiesMatched), + m_validated(validated), + m_regexesCache(regexesCache) { } + + template + bool operator()(const StringType &patternProperty, const Subschema *subschema) const + { + const std::string patternPropertyStr(patternProperty.c_str()); + + // It would be nice to store pre-allocated regex objects in the + // PropertiesConstraint. does std::regex currently support + // custom allocators? Anyway, this isn't an issue here, because Valijson's + // JSON Scheme validator does not yet support custom allocators. + const std::regex r(patternPropertyStr); + + bool matchFound = false; + + // Recursively validate all matching properties + typedef const typename AdapterType::ObjectMember ObjectMember; + for (const ObjectMember m : m_object) { + if (std::regex_search(m.first, r)) { + matchFound = true; + if (m_propertiesMatched) { + m_propertiesMatched->insert(m.first); + } + + // Update context + std::vector newContext = m_context; + newContext.push_back("[" + m.first + "]"); + + // Recursively validate property's value + ValidationVisitor validator(m.second, newContext, m_strictTypes, m_results, m_regexesCache); + if (validator.validateSchema(*subschema)) { + continue; + } + + if (m_results) { + m_results->pushError(m_context, "Failed to validate against schema associated with pattern '" + + patternPropertyStr + "'."); + } + + if (m_validated) { + *m_validated = false; + } + + if (!m_continueOnFailure) { + return false; + } + } + } + + // Allow iteration to terminate if there was not at least one match + if (!matchFound && !m_continueIfUnmatched) { + return false; + } + + return m_continueOnSuccess; + } + + private: + const typename AdapterType::Object &m_object; + const std::vector &m_context; + const bool m_continueOnSuccess; + const bool m_continueOnFailure; + const bool m_continueIfUnmatched; + const bool m_strictTypes; + ValidationResults * const m_results; + std::set * const m_propertiesMatched; + bool * const m_validated; + std::unordered_map& m_regexesCache; + }; + + /** + * @brief Functor to validate object properties against sub-schemas defined + * by a 'properties' constraint + */ + struct ValidatePropertySubschemas + { + ValidatePropertySubschemas( + const typename AdapterType::Object &object, + const std::vector &context, + bool continueOnSuccess, + bool continueOnFailure, + bool continueIfUnmatched, + bool strictTypes, + ValidationResults *results, + std::set *propertiesMatched, + bool *validated, + std::unordered_map& regexesCache) + : m_object(object), + m_context(context), + m_continueOnSuccess(continueOnSuccess), + m_continueOnFailure(continueOnFailure), + m_continueIfUnmatched(continueIfUnmatched), + m_strictTypes(strictTypes), + m_results(results), + m_propertiesMatched(propertiesMatched), + m_validated(validated), + m_regexesCache(regexesCache) { } + + template + bool operator()(const StringType &propertyName, const Subschema *subschema) const + { + const std::string propertyNameKey(propertyName.c_str()); + const typename AdapterType::Object::const_iterator itr = m_object.find(propertyNameKey); + if (itr == m_object.end()) { + return m_continueIfUnmatched; + } + + if (m_propertiesMatched) { + m_propertiesMatched->insert(propertyNameKey); + } + + // Update context + std::vector newContext = m_context; + newContext.push_back("[" + propertyNameKey + "]"); + + // Recursively validate property's value + ValidationVisitor validator(itr->second, newContext, m_strictTypes, m_results, m_regexesCache); + if (validator.validateSchema(*subschema)) { + return m_continueOnSuccess; + } + + if (m_results) { + m_results->pushError(m_context, "Failed to validate against schema associated with property name '" + + propertyNameKey + "'."); + } + + if (m_validated) { + *m_validated = false; + } + + return m_continueOnFailure; + } + + private: + const typename AdapterType::Object &m_object; + const std::vector &m_context; + const bool m_continueOnSuccess; + const bool m_continueOnFailure; + const bool m_continueIfUnmatched; + const bool m_strictTypes; + ValidationResults * const m_results; + std::set * const m_propertiesMatched; + bool * const m_validated; + std::unordered_map& m_regexesCache; + }; + + /** + * @brief Functor to validate schema-based dependencies + */ + struct ValidateSchemaDependencies + { + ValidateSchemaDependencies( + const typename AdapterType::Object &object, + const std::vector &context, + ValidationVisitor &validationVisitor, + ValidationResults *results, + bool *validated) + : m_object(object), + m_context(context), + m_validationVisitor(validationVisitor), + m_results(results), + m_validated(validated) { } + + template + bool operator()(const StringType &propertyName, const Subschema *schemaDependency) const + { + const std::string propertyNameKey(propertyName.c_str()); + if (m_object.find(propertyNameKey) == m_object.end()) { + return true; + } + + if (!m_validationVisitor.validateSchema(*schemaDependency)) { + if (m_validated) { + *m_validated = false; + } + if (m_results) { + m_results->pushError(m_context, "Failed to validate against dependent schema."); + } else { + return false; + } + } + + return true; + } + + private: + const typename AdapterType::Object &m_object; + const std::vector &m_context; + ValidationVisitor &m_validationVisitor; + ValidationResults * const m_results; + bool * const m_validated; + }; + + /** + * @brief Functor that can be used to validate one or more subschemas + * + * This functor is designed to be applied to collections of subschemas + * contained within 'allOf', 'anyOf' and 'oneOf' constraints. + * + * The return value depends on whether a given schema validates, with the + * actual return value for a given case being decided at construction time. + * The return value is used by the 'applyToSubschemas' functions in the + * AllOfConstraint, AnyOfConstraint and OneOfConstrant classes to decide + * whether to terminate early. + * + * The functor uses output parameters (provided at construction) to update + * validation state that may be needed by the caller. + */ + struct ValidateSubschemas + { + ValidateSubschemas( + const AdapterType &adapter, + const std::vector &context, + bool continueOnSuccess, + bool continueOnFailure, + ValidationVisitor &validationVisitor, + ValidationResults *results, + unsigned int *numValidated, + bool *validated) + : m_adapter(adapter), + m_context(context), + m_continueOnSuccess(continueOnSuccess), + m_continueOnFailure(continueOnFailure), + m_validationVisitor(validationVisitor), + m_results(results), + m_numValidated(numValidated), + m_validated(validated) { } + + bool operator()(unsigned int index, const Subschema *subschema) const + { + if (m_validationVisitor.validateSchema(*subschema)) { + if (m_numValidated) { + (*m_numValidated)++; + } + + return m_continueOnSuccess; + } + + if (m_validated) { + *m_validated = false; + } + + if (m_results) { + m_results->pushError(m_context, + "Failed to validate against child schema #" + std::to_string(index) + "."); + } + + return m_continueOnFailure; + } + + private: + const AdapterType &m_adapter; + const std::vector &m_context; + bool m_continueOnSuccess; + bool m_continueOnFailure; + ValidationVisitor &m_validationVisitor; + ValidationResults * const m_results; + unsigned int * const m_numValidated; + bool * const m_validated; + }; + + /** + * @brief Callback function that passes a visitor to a constraint. + * + * @param constraint Reference to constraint to be visited + * @param visitor Reference to visitor to be applied + * + * @return true if the visitor returns successfully, false otherwise. + */ + static bool validationCallback(const constraints::Constraint &constraint, ValidationVisitor &visitor) + { + return constraint.accept(visitor); + } + + /// The JSON value being validated + const AdapterType m_target; + + /// Vector of strings describing the current object context + const std::vector m_context; + + /// Optional pointer to a ValidationResults object to be populated + ValidationResults *m_results; + + /// Option to use strict type comparison + const bool m_strictTypes; + + /// Cached regex objects for pattern constraint + std::unordered_map& m_regexesCache; +}; + +} // namespace valijson + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/subprojects/valijson/include/valijson/validator.hpp b/subprojects/valijson/include/valijson/validator.hpp new file mode 100644 index 000000000..b26f625f2 --- /dev/null +++ b/subprojects/valijson/include/valijson/validator.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +namespace valijson { + +class Schema; +class ValidationResults; + +/** + * @brief Class that provides validation functionality. + */ +class Validator +{ +public: + enum TypeCheckingMode + { + kStrongTypes, + kWeakTypes + }; + + /** + * @brief Construct a Validator that uses strong type checking by default + */ + Validator() + : strictTypes(true) { } + + /** + * @brief Construct a Validator using a specific type checking mode + * + * @param typeCheckingMode choice of strong or weak type checking + */ + Validator(TypeCheckingMode typeCheckingMode) + : strictTypes(typeCheckingMode == kStrongTypes) { } + + /** + * @brief Validate a JSON document and optionally return the results. + * + * When a ValidationResults object is provided via the \c results parameter, + * validation will be performed against each constraint defined by the + * schema, even if validation fails for some or all constraints. + * + * If a pointer to a ValidationResults instance is not provided, validation + * will only continue for as long as the constraints are validated + * successfully. + * + * @param schema The schema to validate against + * @param target A rapidjson::Value to be validated + * + * @param results An optional pointer to a ValidationResults instance that + * will be used to report validation errors + * + * @returns true if validation succeeds, false otherwise + */ + template + bool validate(const Subschema &schema, const AdapterType &target, + ValidationResults *results) + { + // Construct a ValidationVisitor to perform validation at the root level + ValidationVisitor v(target, + std::vector(1, ""), strictTypes, results, regexesCache); + + return v.validateSchema(schema); + } + +private: + + /// Flag indicating that strict type comparisons should be used + const bool strictTypes; + + /// Cached regex objects for pattern constraint. Key - pattern. + std::unordered_map regexesCache; + +}; + +} // namespace valijson diff --git a/subprojects/websocketpp/websocketpp/CMakeLists.txt b/subprojects/websocketpp/websocketpp/CMakeLists.txt new file mode 100644 index 000000000..3ea8cc1c4 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/CMakeLists.txt @@ -0,0 +1,2 @@ +init_target("websocketpp") +final_target () diff --git a/subprojects/websocketpp/websocketpp/COPYING b/subprojects/websocketpp/websocketpp/COPYING new file mode 100644 index 000000000..f8cc5ba1b --- /dev/null +++ b/subprojects/websocketpp/websocketpp/COPYING @@ -0,0 +1,145 @@ +Main Library: + +Copyright (c) 2014, Peter Thorson. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the WebSocket++ Project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Bundled Libraries: + +****** Base 64 Library (base64/base64.hpp) ****** +base64.hpp is a repackaging of the base64.cpp and base64.h files into a +single header suitable for use as a header only library. This conversion was +done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to +the code are redistributed under the same license as the original, which is +listed below. + +base64.cpp and base64.h + +Copyright (C) 2004-2008 René Nyffenegger + +This source code is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + +3. This notice may not be removed or altered from any source distribution. + +René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +****** SHA1 Library (sha1/sha1.hpp) ****** +sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the shallsha1 +library (http://code.google.com/p/smallsha1/) into a single header suitable for +use as a header only library. This conversion was done by Peter Thorson +(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed +under the same license as the original, which is listed below. + + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +****** MD5 Library (common/md5.hpp) ****** +md5.hpp is a reformulation of the md5.h and md5.c code from +http://www.opensource.apple.com/source/cups/cups-59/cups/md5.c to allow it to +function as a component of a header only library. This conversion was done by +Peter Thorson (webmaster@zaphoyd.com) in 2012 for the WebSocket++ project. The +changes are released under the same license as the original (listed below) + +Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +L. Peter Deutsch +ghost@aladdin.com + +****** UTF8 Validation logic (utf8_validation.hpp) ****** +utf8_validation.hpp is adapted from code originally written by Bjoern Hoehrmann +. See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for +details. + +The original license: + +Copyright (c) 2008-2009 Bjoern Hoehrmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/subprojects/websocketpp/websocketpp/base64/base64.hpp b/subprojects/websocketpp/websocketpp/base64/base64.hpp new file mode 100644 index 000000000..ff1561d1c --- /dev/null +++ b/subprojects/websocketpp/websocketpp/base64/base64.hpp @@ -0,0 +1,178 @@ +/* + ****** + base64.hpp is a repackaging of the base64.cpp and base64.h files into a + single header suitable for use as a header only library. This conversion was + done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to + the code are redistributed under the same license as the original, which is + listed below. + ****** + + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#ifndef _BASE64_HPP_ +#define _BASE64_HPP_ + +#include + +namespace websocketpp { + +static std::string const base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/// Test whether a character is a valid base64 character +/** + * @param c The character to test + * @return true if c is a valid base64 character + */ +static inline bool is_base64(unsigned char c) { + return (c == 43 || // + + (c >= 47 && c <= 57) || // /-9 + (c >= 65 && c <= 90) || // A-Z + (c >= 97 && c <= 122)); // a-z +} + +/// Encode a char buffer into a base64 string +/** + * @param input The input data + * @param len The length of input in bytes + * @return A base64 encoded string representing input + */ +inline std::string base64_encode(unsigned char const * input, size_t len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (len--) { + char_array_3[i++] = *(input++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) { + ret += base64_chars[char_array_4[i]]; + } + i = 0; + } + } + + if (i) { + for(j = i; j < 3; j++) { + char_array_3[j] = '\0'; + } + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) { + ret += base64_chars[char_array_4[j]]; + } + + while((i++ < 3)) { + ret += '='; + } + } + + return ret; +} + +/// Encode a string into a base64 string +/** + * @param input The input data + * @return A base64 encoded string representing input + */ +inline std::string base64_encode(std::string const & input) { + return base64_encode( + reinterpret_cast(input.data()), + input.size() + ); +} + +/// Decode a base64 encoded string into a string of raw bytes +/** + * @param input The base64 encoded input data + * @return A string representing the decoded raw bytes + */ +inline std::string base64_decode(std::string const & input) { + size_t in_len = input.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( input[in_] != '=') && is_base64(input[in_])) { + char_array_4[i++] = input[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) { + char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); + } + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) { + ret += char_array_3[i]; + } + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) { + ret += static_cast(char_array_3[j]); + } + } + + return ret; +} + +} // namespace websocketpp + +#endif // _BASE64_HPP_ diff --git a/subprojects/websocketpp/websocketpp/client.hpp b/subprojects/websocketpp/websocketpp/client.hpp new file mode 100644 index 000000000..8782d7e4a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/client.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CLIENT_HPP +#define WEBSOCKETPP_CLIENT_HPP + +#include + +#endif //WEBSOCKETPP_CLIENT_HPP diff --git a/subprojects/websocketpp/websocketpp/close.hpp b/subprojects/websocketpp/websocketpp/close.hpp new file mode 100644 index 000000000..276498e6e --- /dev/null +++ b/subprojects/websocketpp/websocketpp/close.hpp @@ -0,0 +1,353 @@ + +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CLOSE_HPP +#define WEBSOCKETPP_CLOSE_HPP + +/** \file + * A package of types and methods for manipulating WebSocket close codes. + */ + +#include +#include +#include +#include + +#include + +namespace websocketpp { +/// A package of types and methods for manipulating WebSocket close codes. +namespace close { +/// A package of types and methods for manipulating WebSocket close status' +namespace status { + /// The type of a close code value. + typedef uint16_t value; + + /// A blank value for internal use. + static value const blank = 0; + + /// Close the connection without a WebSocket close handshake. + /** + * This special value requests that the WebSocket connection be closed + * without performing the WebSocket closing handshake. This does not comply + * with RFC6455, but should be safe to do if necessary. This could be useful + * for clients that need to disconnect quickly and cannot afford the + * complete handshake. + */ + static value const omit_handshake = 1; + + /// Close the connection with a forced TCP drop. + /** + * This special value requests that the WebSocket connection be closed by + * forcibly dropping the TCP connection. This will leave the other side of + * the connection with a broken connection and some expensive timeouts. this + * should not be done except in extreme cases or in cases of malicious + * remote endpoints. + */ + static value const force_tcp_drop = 2; + + /// Normal closure, meaning that the purpose for which the connection was + /// established has been fulfilled. + static value const normal = 1000; + + /// The endpoint was "going away", such as a server going down or a browser + /// navigating away from a page. + static value const going_away = 1001; + + /// A protocol error occurred. + static value const protocol_error = 1002; + + /// The connection was terminated because an endpoint received a type of + /// data it cannot accept. + /** + * (e.g., an endpoint that understands only text data MAY send this if it + * receives a binary message). + */ + static value const unsupported_data = 1003; + + /// A dummy value to indicate that no status code was received. + /** + * This value is illegal on the wire. + */ + static value const no_status = 1005; + + /// A dummy value to indicate that the connection was closed abnormally. + /** + * In such a case there was no close frame to extract a value from. This + * value is illegal on the wire. + */ + static value const abnormal_close = 1006; + + /// An endpoint received message data inconsistent with its type. + /** + * For example: Invalid UTF8 bytes in a text message. + */ + static value const invalid_payload = 1007; + + /// An endpoint received a message that violated its policy. + /** + * This is a generic status code that can be returned when there is no other + * more suitable status code (e.g., 1003 or 1009) or if there is a need to + * hide specific details about the policy. + */ + static value const policy_violation = 1008; + + /// An endpoint received a message too large to process. + static value const message_too_big = 1009; + + /// A client expected the server to accept a required extension request + /** + * The list of extensions that are needed SHOULD appear in the /reason/ part + * of the Close frame. Note that this status code is not used by the server, + * because it can fail the WebSocket handshake instead. + */ + static value const extension_required = 1010; + + /// An endpoint encountered an unexpected condition that prevented it from + /// fulfilling the request. + static value const internal_endpoint_error = 1011; + + /// Indicates that the service is restarted. A client may reconnect and if + /// if it chooses to do so, should reconnect using a randomized delay of + /// 5-30s + static value const service_restart = 1012; + + /// Indicates that the service is experiencing overload. A client should + /// only connect to a different IP (when there are multiple for the target) + /// or reconnect to the same IP upon user action. + static value const try_again_later = 1013; + + /// Indicates that the server was acting as a gateway or proxy and received + /// an invalid response from the upstream server. This is similar to 502 + /// HTTP Status Code. + static value const bad_gateway = 1014; + + /// An endpoint failed to perform a TLS handshake + /** + * Designated for use in applications expecting a status code to indicate + * that the connection was closed due to a failure to perform a TLS + * handshake (e.g., the server certificate can't be verified). This value is + * illegal on the wire. + */ + static value const tls_handshake = 1015; + + /// A generic subprotocol error + /** + * Indicates that a subprotocol error occurred. Typically this involves + * receiving a message that is not formatted as a valid message for the + * subprotocol in use. + */ + static value const subprotocol_error = 3000; + + /// A invalid subprotocol data + /** + * Indicates that data was received that violated the specification of the + * subprotocol in use. + */ + static value const invalid_subprotocol_data = 3001; + + /// First value in range reserved for future protocol use + static value const rsv_start = 1016; + /// Last value in range reserved for future protocol use + static value const rsv_end = 2999; + + /// Test whether a close code is in a reserved range + /** + * @param [in] code The code to test + * @return Whether or not code is reserved + */ + inline bool reserved(value code) { + return ((code >= rsv_start && code <= rsv_end) || + code == 1004); + } + + /// First value in range that is always invalid on the wire + static value const invalid_low = 999; + /// Last value in range that is always invalid on the wire + static value const invalid_high = 5000; + + /// Test whether a close code is invalid on the wire + /** + * @param [in] code The code to test + * @return Whether or not code is invalid on the wire + */ + inline bool invalid(value code) { + return (code <= invalid_low || code >= invalid_high || + code == no_status || code == abnormal_close || + code == tls_handshake); + } + + /// Determine if the code represents an unrecoverable error + /** + * There is a class of errors for which once they are discovered normal + * WebSocket functionality can no longer occur. This function determines + * if a given code is one of these values. This information is used to + * determine if the system has the capability of waiting for a close + * acknowledgement or if it should drop the TCP connection immediately + * after sending its close frame. + * + * @param [in] code The value to test. + * @return True if the code represents an unrecoverable error + */ + inline bool terminal(value code) { + return (code == protocol_error || code == invalid_payload || + code == policy_violation || code == message_too_big || + code == internal_endpoint_error); + } + + /// Return a human readable interpretation of a WebSocket close code + /** + * See https://tools.ietf.org/html/rfc6455#section-7.4 for more details. + * + * @since 0.3.0 + * + * @param [in] code The code to look up. + * @return A human readable interpretation of the code. + */ + inline std::string get_string(value code) { + switch (code) { + case normal: + return "Normal close"; + case going_away: + return "Going away"; + case protocol_error: + return "Protocol error"; + case unsupported_data: + return "Unsupported data"; + case no_status: + return "No status set"; + case abnormal_close: + return "Abnormal close"; + case invalid_payload: + return "Invalid payload"; + case policy_violation: + return "Policy violoation"; + case message_too_big: + return "Message too big"; + case extension_required: + return "Extension required"; + case internal_endpoint_error: + return "Internal endpoint error"; + case service_restart: + return "Service restart"; + case try_again_later: + return "Try again later"; + case bad_gateway: + return "Bad gateway"; + case tls_handshake: + return "TLS handshake failure"; + case subprotocol_error: + return "Generic subprotocol error"; + case invalid_subprotocol_data: + return "Invalid subprotocol data"; + default: + return "Unknown"; + } + } +} // namespace status + +/// Type used to convert close statuses between integer and wire representations +union code_converter { + uint16_t i; + char c[2]; +}; + +/// Extract a close code value from a close payload +/** + * If there is no close value (ie string is empty) status::no_status is + * returned. If a code couldn't be extracted (usually do to a short or + * otherwise mangled payload) status::protocol_error is returned and the ec + * value is flagged as an error. Note that this case is different than the case + * where protocol error is received over the wire. + * + * If the value is in an invalid or reserved range ec is set accordingly. + * + * @param [in] payload Close frame payload value received over the wire. + * @param [out] ec Set to indicate what error occurred, if any. + * @return The extracted value + */ +inline status::value extract_code(std::string const & payload, lib::error_code + & ec) +{ + ec = lib::error_code(); + + if (payload.size() == 0) { + return status::no_status; + } else if (payload.size() == 1) { + ec = make_error_code(error::bad_close_code); + return status::protocol_error; + } + + code_converter val; + + val.c[0] = payload[0]; + val.c[1] = payload[1]; + + status::value code(ntohs(val.i)); + + if (status::invalid(code)) { + ec = make_error_code(error::invalid_close_code); + } + + if (status::reserved(code)) { + ec = make_error_code(error::reserved_close_code); + } + + return code; +} + +/// Extract the reason string from a close payload +/** + * The string should be a valid UTF8 message. error::invalid_utf8 will be set if + * the function extracts a reason that is not valid UTF8. + * + * @param [in] payload The payload string to extract a reason from. + * @param [out] ec Set to indicate what error occurred, if any. + * @return The reason string. + */ +inline std::string extract_reason(std::string const & payload, lib::error_code + & ec) +{ + std::string reason; + ec = lib::error_code(); + + if (payload.size() > 2) { + reason.append(payload.begin()+2,payload.end()); + } + + if (!websocketpp::utf8_validator::validate(reason)) { + ec = make_error_code(error::invalid_utf8); + } + + return reason; +} + +} // namespace close +} // namespace websocketpp + +#endif // WEBSOCKETPP_CLOSE_HPP diff --git a/subprojects/websocketpp/websocketpp/common/asio.hpp b/subprojects/websocketpp/websocketpp/common/asio.hpp new file mode 100644 index 000000000..3c8fa13e9 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/asio.hpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_ASIO_HPP +#define WEBSOCKETPP_COMMON_ASIO_HPP + +// This file goes to some length to preserve compatibility with versions of +// boost older than 1.49 (where the first modern steady_timer timer based on +// boost/std chrono was introduced. +// +// For the versions older than 1.49, the deadline_timer is used instead. this +// brings in dependencies on boost date_time and it has a different interface +// that is normalized by the `lib::asio::is_neg` and `lib::asio::milliseconds` +// wrappers provided by this file. +// +// The primary reason for this continued support is that boost 1.48 is the +// default and not easily changeable version of boost supplied by the package +// manager of popular Linux distributions like Ubuntu 12.04 LTS. Once the need +// for this has passed this should be cleaned up and simplified. + +#ifdef ASIO_STANDALONE + #include + + #if (ASIO_VERSION/100000) == 1 && ((ASIO_VERSION/100)%1000) < 8 + static_assert(false, "The minimum version of standalone Asio is 1.8.0"); + #endif + + #include + #include + #include +#else + #include + + // See note above about boost <1.49 compatibility. If we are running on + // boost > 1.48 pull in the steady timer and chrono library + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48 + #include + #include + #endif + + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef ASIO_STANDALONE + namespace asio { + using namespace ::asio; + // Here we assume that we will be using std::error_code with standalone + // Asio. This is probably a good assumption, but it is possible in rare + // cases that local Asio versions would be used. + using std::errc; + + // See note above about boost <1.49 compatibility. Because we require + // a standalone Asio version of 1.8+ we are guaranteed to have + // steady_timer available. By convention we require the chrono library + // (either boost or std) for use with standalone Asio. + template + bool is_neg(T duration) { + return duration.count() < 0; + } + inline lib::chrono::milliseconds milliseconds(long duration) { + return lib::chrono::milliseconds(duration); + } + } // namespace asio + +#else + namespace asio { + using namespace boost::asio; + + // See note above about boost <1.49 compatibility + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48 + // Using boost::asio >=1.49 so we use chrono and steady_timer + template + bool is_neg(T duration) { + return duration.count() < 0; + } + + // If boost believes it has std::chrono available it will use it + // so we should also use it for things that relate to boost, even + // if the library would otherwise use boost::chrono. + #if defined(BOOST_ASIO_HAS_STD_CHRONO) + inline std::chrono::milliseconds milliseconds(long duration) { + return std::chrono::milliseconds(duration); + } + #else + inline lib::chrono::milliseconds milliseconds(long duration) { + return lib::chrono::milliseconds(duration); + } + #endif + #else + // Using boost::asio <1.49 we pretend a deadline timer is a steady + // timer and wrap the negative detection and duration conversion + // appropriately. + typedef boost::asio::deadline_timer steady_timer; + + template + bool is_neg(T duration) { + return duration.is_negative(); + } + inline boost::posix_time::time_duration milliseconds(long duration) { + return boost::posix_time::milliseconds(duration); + } + #endif + + using boost::system::error_code; + namespace errc = boost::system::errc; + } // namespace asio +#endif + + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_ASIO_HPP diff --git a/subprojects/websocketpp/websocketpp/common/asio_ssl.hpp b/subprojects/websocketpp/websocketpp/common/asio_ssl.hpp new file mode 100644 index 000000000..698928322 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/asio_ssl.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_ASIO_SSL_HPP +#define WEBSOCKETPP_COMMON_ASIO_SSL_HPP + +// NOTE: This file must be included before common/asio.hpp + +#ifdef ASIO_STANDALONE + #include +#else + #include +#endif + +#endif // WEBSOCKETPP_COMMON_ASIO_SSL_HPP diff --git a/subprojects/websocketpp/websocketpp/common/chrono.hpp b/subprojects/websocketpp/websocketpp/common/chrono.hpp new file mode 100644 index 000000000..975ee043d --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/chrono.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_CHRONO_HPP +#define WEBSOCKETPP_COMMON_CHRONO_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_ + #ifndef _WEBSOCKETPP_CPP11_CHRONO_ + #define _WEBSOCKETPP_CPP11_CHRONO_ + #endif +#endif + +// If we're on Visual Studio 2012 or higher and haven't explicitly disabled +// the use of C++11 chrono header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1700 && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_ + #ifndef _WEBSOCKETPP_CPP11_CHRONO_ + #define _WEBSOCKETPP_CPP11_CHRONO_ + #endif +#endif + +#ifdef _WEBSOCKETPP_CPP11_CHRONO_ + #include +#else + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_CHRONO_ + namespace chrono = std::chrono; +#else + namespace chrono = boost::chrono; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_CHRONO_HPP diff --git a/subprojects/websocketpp/websocketpp/common/connection_hdl.hpp b/subprojects/websocketpp/websocketpp/common/connection_hdl.hpp new file mode 100644 index 000000000..1044c88e3 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/connection_hdl.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP +#define WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP + +#include + +namespace websocketpp { + +/// A handle to uniquely identify a connection. +/** + * This type uniquely identifies a connection. It is implemented as a weak + * pointer to the connection in question. This provides uniqueness across + * multiple endpoints and ensures that IDs never conflict or run out. + * + * It is safe to make copies of this handle, store those copies in containers, + * and use them from other threads. + * + * This handle can be upgraded to a full shared_ptr using + * `endpoint::get_con_from_hdl()` from within a handler fired by the connection + * that owns the handler. + */ +typedef lib::weak_ptr connection_hdl; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP diff --git a/subprojects/websocketpp/websocketpp/common/cpp11.hpp b/subprojects/websocketpp/websocketpp/common/cpp11.hpp new file mode 100644 index 000000000..492a3b8ff --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/cpp11.hpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_CPP11_HPP +#define WEBSOCKETPP_COMMON_CPP11_HPP + +/** + * This header sets up some constants based on the state of C++11 support + */ + +// Hide clang feature detection from other compilers +#ifndef __has_feature // Optional of course. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif +#ifndef __has_extension + #define __has_extension __has_feature // Compatibility with pre-3.0 compilers. +#endif + +// The code below attempts to use information provided by the build system or +// user supplied defines to selectively enable C++11 language and library +// features. In most cases features that are targeted individually may also be +// selectively disabled via an associated _WEBSOCKETPP_NOXXX_ define. + +#if defined(_WEBSOCKETPP_CPP11_STL_) || __cplusplus >= 201103L || defined(_WEBSOCKETPP_CPP11_STRICT_) + // This check tests for blanket c++11 coverage. It can be activated in one + // of three ways. Either the compiler itself reports that it is a full + // C++11 compiler via the __cplusplus macro or the user/build system + // supplies one of the two preprocessor defines below: + + // This is defined to allow other WebSocket++ common headers to enable + // C++11 features when they are detected by this file rather than + // duplicating the above logic in every common header. + #define _WEBSOCKETPP_CPP11_INTERNAL_ + + // _WEBSOCKETPP_CPP11_STRICT_ + // + // This define reports to WebSocket++ that 100% of the language and library + // features of C++11 are available. Using this define on a non-C++11 + // compiler will result in problems. + + // _WEBSOCKETPP_CPP11_STL_ + // + // This define enables *most* C++11 options that were implemented early on + // by compilers. It is typically used for compilers that have many, but not + // all C++11 features. It should be safe to use on GCC 4.7-4.8 and perhaps + // earlier. + #ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_ + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #endif + #ifndef _WEBSOCKETPP_CONSTEXPR_TOKEN_ + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #endif + #ifndef _WEBSOCKETPP_INITIALIZER_LISTS_ + #define _WEBSOCKETPP_INITIALIZER_LISTS_ + #endif + #ifndef _WEBSOCKETPP_NULLPTR_TOKEN_ + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #endif + #ifndef _WEBSOCKETPP_MOVE_SEMANTICS_ + #define _WEBSOCKETPP_MOVE_SEMANTICS_ + #endif + #ifndef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + #define _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + #endif + + #ifndef __GNUC__ + // GCC as of version 4.9 (latest) does not support std::put_time yet. + // so ignore it + #define _WEBSOCKETPP_PUTTIME_ + #endif +#else + // In the absence of a blanket define, try to use compiler versions or + // feature testing macros to selectively enable what we can. + + // Test for noexcept + #ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_ + #ifdef _WEBSOCKETPP_NOEXCEPT_ + // build system says we have noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #else + #if __has_feature(cxx_noexcept) + // clang feature detect says we have noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #elif defined(_MSC_VER) && _MSC_VER >= 1900 + // Visual Studio 2015+ has noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #else + // assume we don't have noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ + #endif + #endif + #endif + + // Test for constexpr + #ifndef _WEBSOCKETPP_CONSTEXPR_TOKEN_ + #ifdef _WEBSOCKETPP_CONSTEXPR_ + // build system says we have constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #else + #if __has_feature(cxx_constexpr) + // clang feature detect says we have constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #elif defined(_MSC_VER) && _MSC_VER >= 1900 + // Visual Studio 2015+ has constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #else + // assume we don't have constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ + #endif + #endif + #endif + + // Enable initializer lists on clang when available. + #if __has_feature(cxx_generalized_initializers) && !defined(_WEBSOCKETPP_INITIALIZER_LISTS_) + #define _WEBSOCKETPP_INITIALIZER_LISTS_ + #endif + + // Test for nullptr + #ifndef _WEBSOCKETPP_NULLPTR_TOKEN_ + #ifdef _WEBSOCKETPP_NULLPTR_ + // build system says we have nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #else + #if __has_feature(cxx_nullptr) + // clang feature detect says we have nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #elif defined(_MSC_VER) &&_MSC_VER >= 1600 + // Visual Studio version that has nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #else + // assume we don't have nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ 0 + #endif + #endif + #endif +#endif + +#endif // WEBSOCKETPP_COMMON_CPP11_HPP diff --git a/subprojects/websocketpp/websocketpp/common/functional.hpp b/subprojects/websocketpp/websocketpp/common/functional.hpp new file mode 100644 index 000000000..d332dd15e --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/functional.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_FUNCTIONAL_HPP +#define WEBSOCKETPP_COMMON_FUNCTIONAL_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_ + #ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #define _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #endif +#endif + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 functional header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_ + #ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #define _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #include +#else + #include + #include + #include +#endif + + + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + using std::function; + using std::bind; + using std::ref; + namespace placeholders = std::placeholders; + + // There are some cases where a C++11 compiler balks at using std::ref + // but a C++03 compiler using boost function requires boost::ref. As such + // lib::ref is not useful in these cases. Instead this macro allows the use + // of boost::ref in the case of a boost compile or no reference wrapper at + // all in the case of a C++11 compile + #define _WEBSOCKETPP_REF(x) x + + template + void clear_function(T & x) { + x = nullptr; + } +#else + using boost::function; + using boost::bind; + using boost::ref; + namespace placeholders { + /// \todo this feels hacky, is there a better way? + using ::_1; + using ::_2; + using ::_3; + } + + // See above definition for more details on what this is and why it exists + #define _WEBSOCKETPP_REF(x) boost::ref(x) + + template + void clear_function(T & x) { + x.clear(); + } +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_FUNCTIONAL_HPP diff --git a/subprojects/websocketpp/websocketpp/common/md5.hpp b/subprojects/websocketpp/websocketpp/common/md5.hpp new file mode 100644 index 000000000..279725f45 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/md5.hpp @@ -0,0 +1,448 @@ +/* + md5.hpp is a reformulation of the md5.h and md5.c code from + http://www.opensource.apple.com/source/cups/cups-59/cups/md5.c to allow it to + function as a component of a header only library. This conversion was done by + Peter Thorson (webmaster@zaphoyd.com) in 2012 for the WebSocket++ project. The + changes are released under the same license as the original (listed below) +*/ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef WEBSOCKETPP_COMMON_MD5_HPP +#define WEBSOCKETPP_COMMON_MD5_HPP + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +#include +#include +#include + +namespace websocketpp { +/// Provides MD5 hashing functionality +namespace md5 { + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +/* Initialize the algorithm. */ +inline void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +inline void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes); + +/* Finish the message and return the digest. */ +inline void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#undef ZSW_MD5_BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define ZSW_MD5_BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define ZSW_MD5_BYTE_ORDER 0 +#endif + +#define ZSW_MD5_T_MASK ((md5_word_t)~0) +#define ZSW_MD5_T1 /* 0xd76aa478 */ (ZSW_MD5_T_MASK ^ 0x28955b87) +#define ZSW_MD5_T2 /* 0xe8c7b756 */ (ZSW_MD5_T_MASK ^ 0x173848a9) +#define ZSW_MD5_T3 0x242070db +#define ZSW_MD5_T4 /* 0xc1bdceee */ (ZSW_MD5_T_MASK ^ 0x3e423111) +#define ZSW_MD5_T5 /* 0xf57c0faf */ (ZSW_MD5_T_MASK ^ 0x0a83f050) +#define ZSW_MD5_T6 0x4787c62a +#define ZSW_MD5_T7 /* 0xa8304613 */ (ZSW_MD5_T_MASK ^ 0x57cfb9ec) +#define ZSW_MD5_T8 /* 0xfd469501 */ (ZSW_MD5_T_MASK ^ 0x02b96afe) +#define ZSW_MD5_T9 0x698098d8 +#define ZSW_MD5_T10 /* 0x8b44f7af */ (ZSW_MD5_T_MASK ^ 0x74bb0850) +#define ZSW_MD5_T11 /* 0xffff5bb1 */ (ZSW_MD5_T_MASK ^ 0x0000a44e) +#define ZSW_MD5_T12 /* 0x895cd7be */ (ZSW_MD5_T_MASK ^ 0x76a32841) +#define ZSW_MD5_T13 0x6b901122 +#define ZSW_MD5_T14 /* 0xfd987193 */ (ZSW_MD5_T_MASK ^ 0x02678e6c) +#define ZSW_MD5_T15 /* 0xa679438e */ (ZSW_MD5_T_MASK ^ 0x5986bc71) +#define ZSW_MD5_T16 0x49b40821 +#define ZSW_MD5_T17 /* 0xf61e2562 */ (ZSW_MD5_T_MASK ^ 0x09e1da9d) +#define ZSW_MD5_T18 /* 0xc040b340 */ (ZSW_MD5_T_MASK ^ 0x3fbf4cbf) +#define ZSW_MD5_T19 0x265e5a51 +#define ZSW_MD5_T20 /* 0xe9b6c7aa */ (ZSW_MD5_T_MASK ^ 0x16493855) +#define ZSW_MD5_T21 /* 0xd62f105d */ (ZSW_MD5_T_MASK ^ 0x29d0efa2) +#define ZSW_MD5_T22 0x02441453 +#define ZSW_MD5_T23 /* 0xd8a1e681 */ (ZSW_MD5_T_MASK ^ 0x275e197e) +#define ZSW_MD5_T24 /* 0xe7d3fbc8 */ (ZSW_MD5_T_MASK ^ 0x182c0437) +#define ZSW_MD5_T25 0x21e1cde6 +#define ZSW_MD5_T26 /* 0xc33707d6 */ (ZSW_MD5_T_MASK ^ 0x3cc8f829) +#define ZSW_MD5_T27 /* 0xf4d50d87 */ (ZSW_MD5_T_MASK ^ 0x0b2af278) +#define ZSW_MD5_T28 0x455a14ed +#define ZSW_MD5_T29 /* 0xa9e3e905 */ (ZSW_MD5_T_MASK ^ 0x561c16fa) +#define ZSW_MD5_T30 /* 0xfcefa3f8 */ (ZSW_MD5_T_MASK ^ 0x03105c07) +#define ZSW_MD5_T31 0x676f02d9 +#define ZSW_MD5_T32 /* 0x8d2a4c8a */ (ZSW_MD5_T_MASK ^ 0x72d5b375) +#define ZSW_MD5_T33 /* 0xfffa3942 */ (ZSW_MD5_T_MASK ^ 0x0005c6bd) +#define ZSW_MD5_T34 /* 0x8771f681 */ (ZSW_MD5_T_MASK ^ 0x788e097e) +#define ZSW_MD5_T35 0x6d9d6122 +#define ZSW_MD5_T36 /* 0xfde5380c */ (ZSW_MD5_T_MASK ^ 0x021ac7f3) +#define ZSW_MD5_T37 /* 0xa4beea44 */ (ZSW_MD5_T_MASK ^ 0x5b4115bb) +#define ZSW_MD5_T38 0x4bdecfa9 +#define ZSW_MD5_T39 /* 0xf6bb4b60 */ (ZSW_MD5_T_MASK ^ 0x0944b49f) +#define ZSW_MD5_T40 /* 0xbebfbc70 */ (ZSW_MD5_T_MASK ^ 0x4140438f) +#define ZSW_MD5_T41 0x289b7ec6 +#define ZSW_MD5_T42 /* 0xeaa127fa */ (ZSW_MD5_T_MASK ^ 0x155ed805) +#define ZSW_MD5_T43 /* 0xd4ef3085 */ (ZSW_MD5_T_MASK ^ 0x2b10cf7a) +#define ZSW_MD5_T44 0x04881d05 +#define ZSW_MD5_T45 /* 0xd9d4d039 */ (ZSW_MD5_T_MASK ^ 0x262b2fc6) +#define ZSW_MD5_T46 /* 0xe6db99e5 */ (ZSW_MD5_T_MASK ^ 0x1924661a) +#define ZSW_MD5_T47 0x1fa27cf8 +#define ZSW_MD5_T48 /* 0xc4ac5665 */ (ZSW_MD5_T_MASK ^ 0x3b53a99a) +#define ZSW_MD5_T49 /* 0xf4292244 */ (ZSW_MD5_T_MASK ^ 0x0bd6ddbb) +#define ZSW_MD5_T50 0x432aff97 +#define ZSW_MD5_T51 /* 0xab9423a7 */ (ZSW_MD5_T_MASK ^ 0x546bdc58) +#define ZSW_MD5_T52 /* 0xfc93a039 */ (ZSW_MD5_T_MASK ^ 0x036c5fc6) +#define ZSW_MD5_T53 0x655b59c3 +#define ZSW_MD5_T54 /* 0x8f0ccc92 */ (ZSW_MD5_T_MASK ^ 0x70f3336d) +#define ZSW_MD5_T55 /* 0xffeff47d */ (ZSW_MD5_T_MASK ^ 0x00100b82) +#define ZSW_MD5_T56 /* 0x85845dd1 */ (ZSW_MD5_T_MASK ^ 0x7a7ba22e) +#define ZSW_MD5_T57 0x6fa87e4f +#define ZSW_MD5_T58 /* 0xfe2ce6e0 */ (ZSW_MD5_T_MASK ^ 0x01d3191f) +#define ZSW_MD5_T59 /* 0xa3014314 */ (ZSW_MD5_T_MASK ^ 0x5cfebceb) +#define ZSW_MD5_T60 0x4e0811a1 +#define ZSW_MD5_T61 /* 0xf7537e82 */ (ZSW_MD5_T_MASK ^ 0x08ac817d) +#define ZSW_MD5_T62 /* 0xbd3af235 */ (ZSW_MD5_T_MASK ^ 0x42c50dca) +#define ZSW_MD5_T63 0x2ad7d2bb +#define ZSW_MD5_T64 /* 0xeb86d391 */ (ZSW_MD5_T_MASK ^ 0x14792c6e) + +static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) { + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if ZSW_MD5_BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + md5_word_t const * X; +#endif + + { +#if ZSW_MD5_BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static int const w = 1; + + if (*((md5_byte_t const *)&w)) /* dynamic little-endian */ +#endif +#if ZSW_MD5_BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (md5_byte_t const *)0) & 3)) { + /* data are properly aligned */ + X = (md5_word_t const *)data; + } else { + /* not aligned */ + std::memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if ZSW_MD5_BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if ZSW_MD5_BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if ZSW_MD5_BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ZSW_MD5_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_F(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, ZSW_MD5_T1); + SET(d, a, b, c, 1, 12, ZSW_MD5_T2); + SET(c, d, a, b, 2, 17, ZSW_MD5_T3); + SET(b, c, d, a, 3, 22, ZSW_MD5_T4); + SET(a, b, c, d, 4, 7, ZSW_MD5_T5); + SET(d, a, b, c, 5, 12, ZSW_MD5_T6); + SET(c, d, a, b, 6, 17, ZSW_MD5_T7); + SET(b, c, d, a, 7, 22, ZSW_MD5_T8); + SET(a, b, c, d, 8, 7, ZSW_MD5_T9); + SET(d, a, b, c, 9, 12, ZSW_MD5_T10); + SET(c, d, a, b, 10, 17, ZSW_MD5_T11); + SET(b, c, d, a, 11, 22, ZSW_MD5_T12); + SET(a, b, c, d, 12, 7, ZSW_MD5_T13); + SET(d, a, b, c, 13, 12, ZSW_MD5_T14); + SET(c, d, a, b, 14, 17, ZSW_MD5_T15); + SET(b, c, d, a, 15, 22, ZSW_MD5_T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_G(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, ZSW_MD5_T17); + SET(d, a, b, c, 6, 9, ZSW_MD5_T18); + SET(c, d, a, b, 11, 14, ZSW_MD5_T19); + SET(b, c, d, a, 0, 20, ZSW_MD5_T20); + SET(a, b, c, d, 5, 5, ZSW_MD5_T21); + SET(d, a, b, c, 10, 9, ZSW_MD5_T22); + SET(c, d, a, b, 15, 14, ZSW_MD5_T23); + SET(b, c, d, a, 4, 20, ZSW_MD5_T24); + SET(a, b, c, d, 9, 5, ZSW_MD5_T25); + SET(d, a, b, c, 14, 9, ZSW_MD5_T26); + SET(c, d, a, b, 3, 14, ZSW_MD5_T27); + SET(b, c, d, a, 8, 20, ZSW_MD5_T28); + SET(a, b, c, d, 13, 5, ZSW_MD5_T29); + SET(d, a, b, c, 2, 9, ZSW_MD5_T30); + SET(c, d, a, b, 7, 14, ZSW_MD5_T31); + SET(b, c, d, a, 12, 20, ZSW_MD5_T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_H(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, ZSW_MD5_T33); + SET(d, a, b, c, 8, 11, ZSW_MD5_T34); + SET(c, d, a, b, 11, 16, ZSW_MD5_T35); + SET(b, c, d, a, 14, 23, ZSW_MD5_T36); + SET(a, b, c, d, 1, 4, ZSW_MD5_T37); + SET(d, a, b, c, 4, 11, ZSW_MD5_T38); + SET(c, d, a, b, 7, 16, ZSW_MD5_T39); + SET(b, c, d, a, 10, 23, ZSW_MD5_T40); + SET(a, b, c, d, 13, 4, ZSW_MD5_T41); + SET(d, a, b, c, 0, 11, ZSW_MD5_T42); + SET(c, d, a, b, 3, 16, ZSW_MD5_T43); + SET(b, c, d, a, 6, 23, ZSW_MD5_T44); + SET(a, b, c, d, 9, 4, ZSW_MD5_T45); + SET(d, a, b, c, 12, 11, ZSW_MD5_T46); + SET(c, d, a, b, 15, 16, ZSW_MD5_T47); + SET(b, c, d, a, 2, 23, ZSW_MD5_T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_I(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, ZSW_MD5_T49); + SET(d, a, b, c, 7, 10, ZSW_MD5_T50); + SET(c, d, a, b, 14, 15, ZSW_MD5_T51); + SET(b, c, d, a, 5, 21, ZSW_MD5_T52); + SET(a, b, c, d, 12, 6, ZSW_MD5_T53); + SET(d, a, b, c, 3, 10, ZSW_MD5_T54); + SET(c, d, a, b, 10, 15, ZSW_MD5_T55); + SET(b, c, d, a, 1, 21, ZSW_MD5_T56); + SET(a, b, c, d, 8, 6, ZSW_MD5_T57); + SET(d, a, b, c, 15, 10, ZSW_MD5_T58); + SET(c, d, a, b, 6, 15, ZSW_MD5_T59); + SET(b, c, d, a, 13, 21, ZSW_MD5_T60); + SET(a, b, c, d, 4, 6, ZSW_MD5_T61); + SET(d, a, b, c, 11, 10, ZSW_MD5_T62); + SET(c, d, a, b, 2, 15, ZSW_MD5_T63); + SET(b, c, d, a, 9, 21, ZSW_MD5_T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void md5_init(md5_state_t *pms) { + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ ZSW_MD5_T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ ZSW_MD5_T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes) { + md5_byte_t const * p = data; + size_t left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : static_cast(nbytes)); + + std::memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + std::memcpy(pms->buf, p, left); +} + +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { + static md5_byte_t const pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} + +// some convenience c++ functions +inline std::string md5_hash_string(std::string const & s) { + char digest[16]; + + md5_state_t state; + + md5_init(&state); + md5_append(&state, (md5_byte_t const *)s.c_str(), s.size()); + md5_finish(&state, (md5_byte_t *)digest); + + std::string ret; + ret.resize(16); + std::copy(digest,digest+16,ret.begin()); + + return ret; +} + +const char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + +inline std::string md5_hash_hex(std::string const & input) { + std::string hash = md5_hash_string(input); + std::string hex; + + for (size_t i = 0; i < hash.size(); i++) { + hex.push_back(hexval[((hash[i] >> 4) & 0xF)]); + hex.push_back(hexval[(hash[i]) & 0x0F]); + } + + return hex; +} + +} // md5 +} // websocketpp + +#endif // WEBSOCKETPP_COMMON_MD5_HPP diff --git a/subprojects/websocketpp/websocketpp/common/memory.hpp b/subprojects/websocketpp/websocketpp/common/memory.hpp new file mode 100644 index 000000000..42048e337 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/memory.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_MEMORY_HPP +#define WEBSOCKETPP_COMMON_MEMORY_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 memory header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_ + #ifndef _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_MEMORY_ + #endif +#endif + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 functional header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_ + #ifndef _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_MEMORY_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + using std::shared_ptr; + using std::weak_ptr; + using std::enable_shared_from_this; + using std::static_pointer_cast; + using std::make_shared; + using std::unique_ptr; + + typedef std::unique_ptr unique_ptr_uchar_array; +#else + using boost::shared_ptr; + using boost::weak_ptr; + using std::auto_ptr; + using boost::enable_shared_from_this; + using boost::static_pointer_cast; + using boost::make_shared; + + typedef boost::scoped_array unique_ptr_uchar_array; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_MEMORY_HPP diff --git a/subprojects/websocketpp/websocketpp/common/network.hpp b/subprojects/websocketpp/websocketpp/common/network.hpp new file mode 100644 index 000000000..26a4cb9ea --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/network.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_NETWORK_HPP +#define WEBSOCKETPP_COMMON_NETWORK_HPP + +// For ntohs and htons +#if defined(_WIN32) + #include +#else + //#include + #include +#endif + +#include + +namespace websocketpp { +namespace lib { +namespace net { + +inline bool is_little_endian() { + short int val = 0x1; + char *ptr = reinterpret_cast(&val); + return (ptr[0] == 1); +} + +#define TYP_INIT 0 +#define TYP_SMLE 1 +#define TYP_BIGE 2 + +/// Convert 64 bit value to network byte order +/** + * This method is prefixed to avoid conflicts with operating system level + * macros for this functionality. + * + * TODO: figure out if it would be beneficial to use operating system level + * macros for this. + * + * @param src The integer in host byte order + * @return src converted to network byte order + */ +inline uint64_t _htonll(uint64_t src) { + static int typ = TYP_INIT; + unsigned char c; + union { + uint64_t ull; + unsigned char c[8]; + } x; + if (typ == TYP_INIT) { + x.ull = 0x01; + typ = (x.c[7] == 0x01ULL) ? TYP_BIGE : TYP_SMLE; + } + if (typ == TYP_BIGE) + return src; + x.ull = src; + c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; + c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; + c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c; + c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c; + return x.ull; +} + +/// Convert 64 bit value to host byte order +/** + * This method is prefixed to avoid conflicts with operating system level + * macros for this functionality. + * + * TODO: figure out if it would be beneficial to use operating system level + * macros for this. + * + * @param src The integer in network byte order + * @return src converted to host byte order + */ +inline uint64_t _ntohll(uint64_t src) { + return _htonll(src); +} + +} // net +} // lib +} // websocketpp + +#endif // WEBSOCKETPP_COMMON_NETWORK_HPP diff --git a/subprojects/websocketpp/websocketpp/common/platforms.hpp b/subprojects/websocketpp/websocketpp/common/platforms.hpp new file mode 100644 index 000000000..877985777 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/platforms.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_PLATFORMS_HPP +#define WEBSOCKETPP_COMMON_PLATFORMS_HPP + +/** + * This header contains any platform specific preprocessor adjustments that + * don't fit somewhere else better. + */ + +#if defined(_WIN32) && !defined(NOMINMAX) + // don't define min and max macros that conflict with std::min and std::max + #define NOMINMAX +#endif + +// Bump up the variadic parameter max for Visual Studio 2012 +#if defined(_MSC_VER) && _MSC_VER == 1700 + #define _VARIADIC_MAX 8 +#endif + +#endif // WEBSOCKETPP_COMMON_PLATFORMS_HPP diff --git a/subprojects/websocketpp/websocketpp/common/random.hpp b/subprojects/websocketpp/websocketpp/common/random.hpp new file mode 100644 index 000000000..ddf996941 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/random.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP +#define WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 random header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_RANDOM_DEVICE_ + #ifndef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + #define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + #endif +#endif + + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 random header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_ + #ifndef _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_MEMORY_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + #include +#else + #include + + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 46 + #include + #include + #elif (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) >= 43 + #include + #else + // TODO: static_assert(false, "Could not find a suitable random_device") + #endif +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + using std::random_device; + using std::uniform_int_distribution; +#else + using boost::random::random_device; + using boost::random::uniform_int_distribution; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP diff --git a/subprojects/websocketpp/websocketpp/common/regex.hpp b/subprojects/websocketpp/websocketpp/common/regex.hpp new file mode 100644 index 000000000..326635de2 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/regex.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_REGEX_HPP +#define WEBSOCKETPP_COMMON_REGEX_HPP + +#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_REGEX_ + #ifndef _WEBSOCKETPP_CPP11_REGEX_ + #define _WEBSOCKETPP_CPP11_REGEX_ + #endif +#endif + +#ifdef _WEBSOCKETPP_CPP11_REGEX_ + #include +#else + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_REGEX_ + using std::cmatch; + using std::regex; + using std::regex_match; +#else + using boost::cmatch; + using boost::regex; + using boost::regex_match; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_REGEX_HPP diff --git a/subprojects/websocketpp/websocketpp/common/stdint.hpp b/subprojects/websocketpp/websocketpp/common/stdint.hpp new file mode 100644 index 000000000..ec48ea75b --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/stdint.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_STDINT_HPP +#define WEBSOCKETPP_COMMON_STDINT_HPP + +#ifndef __STDC_LIMIT_MACROS + #define __STDC_LIMIT_MACROS 1 +#endif + +#if defined (_WIN32) && defined (_MSC_VER) && (_MSC_VER < 1600) + #include + + using boost::int8_t; + using boost::int_least8_t; + using boost::int_fast8_t; + using boost::uint8_t; + using boost::uint_least8_t; + using boost::uint_fast8_t; + + using boost::int16_t; + using boost::int_least16_t; + using boost::int_fast16_t; + using boost::uint16_t; + using boost::uint_least16_t; + using boost::uint_fast16_t; + + using boost::int32_t; + using boost::int_least32_t; + using boost::int_fast32_t; + using boost::uint32_t; + using boost::uint_least32_t; + using boost::uint_fast32_t; + + #ifndef BOOST_NO_INT64_T + using boost::int64_t; + using boost::int_least64_t; + using boost::int_fast64_t; + using boost::uint64_t; + using boost::uint_least64_t; + using boost::uint_fast64_t; + #endif + using boost::intmax_t; + using boost::uintmax_t; +#else + #include +#endif + +#endif // WEBSOCKETPP_COMMON_STDINT_HPP diff --git a/subprojects/websocketpp/websocketpp/common/system_error.hpp b/subprojects/websocketpp/websocketpp/common/system_error.hpp new file mode 100644 index 000000000..e5aea2549 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/system_error.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP +#define WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP + + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 system_error header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_ + #ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #endif +#endif + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 system_error header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_ + #ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #include +#else + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + using std::errc; + using std::error_code; + using std::error_category; + using std::error_condition; + using std::system_error; + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace std { + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ } +#else + namespace errc = boost::system::errc; + using boost::system::error_code; + using boost::system::error_category; + using boost::system::error_condition; + using boost::system::system_error; + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace boost { namespace system { + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ }} +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP diff --git a/subprojects/websocketpp/websocketpp/common/thread.hpp b/subprojects/websocketpp/websocketpp/common/thread.hpp new file mode 100644 index 000000000..9cf959ce0 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/thread.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_THREAD_HPP +#define WEBSOCKETPP_COMMON_THREAD_HPP + +#include + +// If we autodetect C++11 and haven't been explicitly instructed to not use +// C++11 threads, then set the defines that instructs the rest of this header +// to use C++11 and +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_THREAD_ + // MinGW by default does not support C++11 thread/mutex so even if the + // internal check for C++11 passes, ignore it if we are on MinGW + #if (!defined(__MINGW32__) && !defined(__MINGW64__)) || __MINGW64_VERSION_MAJOR>=6 + #ifndef _WEBSOCKETPP_CPP11_THREAD_ + #define _WEBSOCKETPP_CPP11_THREAD_ + #endif + #endif +#endif + +// If we're on Visual Studio 2013 or higher and haven't explicitly disabled +// the use of C++11 thread header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1800 && !defined _WEBSOCKETPP_NO_CPP11_THREAD_ + #ifndef _WEBSOCKETPP_CPP11_THREAD_ + #define _WEBSOCKETPP_CPP11_THREAD_ + #endif +#endif + +#if defined(_WEBSOCKETPP_MINGW_THREAD_) + #include + #include + #include +#elif defined(_WEBSOCKETPP_CPP11_THREAD_) + #include + #include + #include +#else + #include + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#if defined(_WEBSOCKETPP_CPP11_THREAD_) || defined(_WEBSOCKETPP_MINGW_THREAD_) + using std::mutex; + using std::lock_guard; + using std::thread; + using std::unique_lock; + using std::condition_variable; +#else + using boost::mutex; + using boost::lock_guard; + using boost::thread; + using boost::unique_lock; + using boost::condition_variable; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_THREAD_HPP diff --git a/subprojects/websocketpp/websocketpp/common/time.hpp b/subprojects/websocketpp/websocketpp/common/time.hpp new file mode 100644 index 000000000..571688e1b --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/time.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_TIME_HPP +#define WEBSOCKETPP_COMMON_TIME_HPP + +#include + +namespace websocketpp { +namespace lib { + +// Code in this header was inspired by the following article and includes some +// code from the related project g2log. The g2log code is public domain licensed +// http://kjellkod.wordpress.com/2013/01/22/exploring-c11-part-2-localtime-and-time-again/ + +/// Thread safe cross platform localtime +inline std::tm localtime(std::time_t const & time) { + std::tm tm_snapshot; +#if (defined(__MINGW32__) || defined(__MINGW64__)) + memcpy(&tm_snapshot, ::localtime(&time), sizeof(std::tm)); +#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + localtime_s(&tm_snapshot, &time); +#else + localtime_r(&time, &tm_snapshot); // POSIX +#endif + return tm_snapshot; +} + +} // lib +} // websocketpp + +#endif // WEBSOCKETPP_COMMON_TIME_HPP diff --git a/subprojects/websocketpp/websocketpp/common/type_traits.hpp b/subprojects/websocketpp/websocketpp/common/type_traits.hpp new file mode 100644 index 000000000..c89b82b31 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/common/type_traits.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP +#define WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_TYPE_TRAITS_ + #ifndef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #define _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #endif +#endif + + +#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #include +#else + #include +#endif + + + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + using std::aligned_storage; + using std::is_same; +#else + using boost::aligned_storage; + using boost::is_same; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP diff --git a/subprojects/websocketpp/websocketpp/concurrency/basic.hpp b/subprojects/websocketpp/websocketpp/concurrency/basic.hpp new file mode 100644 index 000000000..1943ad77a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/concurrency/basic.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONCURRENCY_BASIC_HPP +#define WEBSOCKETPP_CONCURRENCY_BASIC_HPP + +#include + +namespace websocketpp { +namespace concurrency { + +/// Concurrency policy that uses std::mutex / boost::mutex +class basic { +public: + typedef lib::mutex mutex_type; + typedef lib::lock_guard scoped_lock_type; +}; + +} // namespace concurrency +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONCURRENCY_BASIC_HPP diff --git a/subprojects/websocketpp/websocketpp/concurrency/none.hpp b/subprojects/websocketpp/websocketpp/concurrency/none.hpp new file mode 100644 index 000000000..da9aa4112 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/concurrency/none.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONCURRENCY_NONE_HPP +#define WEBSOCKETPP_CONCURRENCY_NONE_HPP + +namespace websocketpp { + +/// Concurrency handling support +namespace concurrency { + +/// Implementation for no-op locking primitives +namespace none_impl { +/// A fake mutex implementation that does nothing +class fake_mutex { +public: + fake_mutex() {} + ~fake_mutex() {} +}; + +/// A fake lock guard implementation that does nothing +class fake_lock_guard { +public: + explicit fake_lock_guard(fake_mutex) {} + ~fake_lock_guard() {} +}; +} // namespace none_impl + +/// Stub concurrency policy that implements the interface using no-ops. +/** + * This policy documents the concurrency policy interface using no-ops. It can + * be used as a reference or base for building a new concurrency policy. It can + * also be used as is to disable all locking for endpoints used in purely single + * threaded programs. + */ +class none { +public: + /// The type of a mutex primitive + /** + * std::mutex is an example. + */ + typedef none_impl::fake_mutex mutex_type; + + /// The type of a scoped/RAII lock primitive. + /** + * The scoped lock constructor should take a mutex_type as a parameter, + * acquire that lock, and release it in its destructor. std::lock_guard is + * an example. + */ + typedef none_impl::fake_lock_guard scoped_lock_type; +}; + +} // namespace concurrency +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONCURRENCY_ASYNC_HPP diff --git a/subprojects/websocketpp/websocketpp/config/asio.hpp b/subprojects/websocketpp/websocketpp/config/asio.hpp new file mode 100644 index 000000000..d28d0fb80 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/asio.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_HPP +#define WEBSOCKETPP_CONFIG_ASIO_TLS_HPP + +#include +#include +#include + +// Pull in non-tls config +#include + +// Define TLS config +namespace websocketpp { +namespace config { + +/// Server config with asio transport and TLS enabled +struct asio_tls : public core { + typedef asio_tls type; + typedef core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_HPP diff --git a/subprojects/websocketpp/websocketpp/config/asio_client.hpp b/subprojects/websocketpp/websocketpp/config/asio_client.hpp new file mode 100644 index 000000000..1cb594d0a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/asio_client.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP + +#include +#include +#include + +// Pull in non-tls config +#include + +// Define TLS config +namespace websocketpp { +namespace config { + +/// Client config with asio transport and TLS enabled +struct asio_tls_client : public core_client { + typedef asio_tls_client type; + typedef core_client base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP diff --git a/subprojects/websocketpp/websocketpp/config/asio_no_tls.hpp b/subprojects/websocketpp/websocketpp/config/asio_no_tls.hpp new file mode 100644 index 000000000..6c1357fba --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/asio_no_tls.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_HPP +#define WEBSOCKETPP_CONFIG_ASIO_HPP + +#include +#include + +namespace websocketpp { +namespace config { + +/// Server config with asio transport and TLS disabled +struct asio : public core { + typedef asio type; + typedef core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_HPP diff --git a/subprojects/websocketpp/websocketpp/config/asio_no_tls_client.hpp b/subprojects/websocketpp/websocketpp/config/asio_no_tls_client.hpp new file mode 100644 index 000000000..6e3f7ba0e --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/asio_no_tls_client.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP + +#include +#include + +namespace websocketpp { +namespace config { + +/// Client config with asio transport and TLS disabled +struct asio_client : public core_client { + typedef asio_client type; + typedef core_client base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP diff --git a/subprojects/websocketpp/websocketpp/config/boost_config.hpp b/subprojects/websocketpp/websocketpp/config/boost_config.hpp new file mode 100644 index 000000000..57671ccda --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/boost_config.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + // This header defines WebSocket++ macros for C++11 compatibility based on the + // Boost.Config library. This will correctly configure most target platforms + // simply by including this header before any other WebSocket++ header. + +#ifndef WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP +#define WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP + +#include + +// _WEBSOCKETPP_CPP11_MEMORY_ and _WEBSOCKETPP_CPP11_FUNCTIONAL_ presently +// only work if either both or neither is defined. +#if !defined BOOST_NO_CXX11_SMART_PTR && !defined BOOST_NO_CXX11_HDR_FUNCTIONAL + #define _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_FUNCTIONAL_ +#endif + +#ifdef BOOST_ASIO_HAS_STD_CHRONO + #define _WEBSOCKETPP_CPP11_CHRONO_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_RANDOM + #define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_REGEX + #define _WEBSOCKETPP_CPP11_REGEX_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_SYSTEM_ERROR + #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_THREAD + #define _WEBSOCKETPP_CPP11_THREAD_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST + #define _WEBSOCKETPP_INITIALIZER_LISTS_ +#endif + +#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ BOOST_NOEXCEPT +#define _WEBSOCKETPP_CONSTEXPR_TOKEN_ BOOST_CONSTEXPR +// TODO: nullptr support + +#endif // WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP diff --git a/subprojects/websocketpp/websocketpp/config/core.hpp b/subprojects/websocketpp/websocketpp/config/core.hpp new file mode 100644 index 000000000..93981aa0e --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/core.hpp @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_CORE_HPP +#define WEBSOCKETPP_CONFIG_CORE_HPP + +// Non-Policy common stuff +#include +#include +#include + +// Concurrency +#include + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Server config with iostream transport +struct core { + typedef core type; + + // Concurrency policy + typedef websocketpp::concurrency::basic concurrency_type; + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::basic elog_type; + typedef websocketpp::log::basic alog_type; + + /// RNG policies + typedef websocketpp::random::none::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::iostream::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; + + /// Size of the per-connection read buffer + /** + * Each connection has an internal buffer of this size. A larger value will + * result in fewer trips through the library and less CPU overhead at the + * expense of increased memory usage per connection. + * + * If your application primarily deals in very large messages you may want + * to try setting this value higher. + * + * If your application has a lot of connections or primarily deals in small + * messages you may want to try setting this smaller. + */ + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.3.0 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_compress extension + struct permessage_deflate_config { + typedef core::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-deflate + /** + * Automatically enables the permessage-deflate extension. + * + * For clients this results in a permessage-deflate extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_CORE_HPP diff --git a/subprojects/websocketpp/websocketpp/config/core_client.hpp b/subprojects/websocketpp/websocketpp/config/core_client.hpp new file mode 100644 index 000000000..dadf8a4e7 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/core_client.hpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP + +// Non-Policy common stuff +#include +#include +#include + +// Concurrency +#ifndef _WEBSOCKETPP_NO_THREADING_ +#include +#else +#include +#endif + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Client config with iostream transport +struct core_client { + typedef core_client type; + + // Concurrency policy +#ifndef _WEBSOCKETPP_NO_THREADING_ + typedef websocketpp::concurrency::basic concurrency_type; +#else + typedef websocketpp::concurrency::none concurrency_type; +#endif + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::basic elog_type; + typedef websocketpp::log::basic alog_type; + + /// RNG policies + typedef websocketpp::random::random_device::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization code + /// Disabling can provide a minor performance improvement to single threaded + /// applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::iostream::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; + + /// + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.3.0 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_deflate extension + struct permessage_deflate_config { + typedef core_client::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-compress + /** + * Automatically enables the permessage-compress extension. + * + * For clients this results in a permessage-compress extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP diff --git a/subprojects/websocketpp/websocketpp/config/debug.hpp b/subprojects/websocketpp/websocketpp/config/debug.hpp new file mode 100644 index 000000000..223f72fbb --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/debug.hpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_DEBUG_HPP +#define WEBSOCKETPP_CONFIG_DEBUG_HPP + + + +// Non-Policy common stuff +#include +#include + +// Concurrency +#include + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Client/Server debug config with iostream transport +struct debug_core { + typedef debug_core type; + + // Concurrency policy + typedef websocketpp::concurrency::basic concurrency_type; + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::basic elog_type; + typedef websocketpp::log::basic alog_type; + + /// RNG policies + typedef websocketpp::random::none::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::iostream::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::all; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::all; + + /// + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.3.0 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_compress extension + struct permessage_deflate_config { + typedef type::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-deflate + /** + * Automatically enables the permessage-deflate extension. + * + * For clients this results in a permessage-deflate extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_CORE_HPP diff --git a/subprojects/websocketpp/websocketpp/config/debug_asio.hpp b/subprojects/websocketpp/websocketpp/config/debug_asio.hpp new file mode 100644 index 000000000..a57c736ce --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/debug_asio.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP +#define WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP + +#include +#include +#include + +// Pull in non-tls config +#include + +// Define TLS config +namespace websocketpp { +namespace config { + +/// Client/Server debug config with asio transport and TLS enabled +struct debug_asio_tls : public debug_core { + typedef debug_asio_tls type; + typedef debug_core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP diff --git a/subprojects/websocketpp/websocketpp/config/debug_asio_no_tls.hpp b/subprojects/websocketpp/websocketpp/config/debug_asio_no_tls.hpp new file mode 100644 index 000000000..b3dc83b49 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/debug_asio_no_tls.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP +#define WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP + +#include +#include + +namespace websocketpp { +namespace config { + +/// Client/Server debug config with asio transport and TLS disabled +struct debug_asio : public debug_core { + typedef debug_asio type; + typedef debug_core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP diff --git a/subprojects/websocketpp/websocketpp/config/minimal_client.hpp b/subprojects/websocketpp/websocketpp/config/minimal_client.hpp new file mode 100644 index 000000000..72528cdee --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/minimal_client.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP + +#include + +namespace websocketpp { +namespace config { + +/// Client config with minimal dependencies +/** + * This config strips out as many dependencies as possible. It is suitable for + * use as a base class for custom configs that want to implement or choose their + * own policies for components that even the core config includes. + * + * NOTE: this config stubs out enough that it cannot be used directly. You must + * supply at least a transport policy and a cryptographically secure random + * number generation policy for a config based on `minimal_client` to do + * anything useful. + * + * Present dependency list for minimal_server config: + * + * C++98 STL: + * + * + * + * + * + * + * C++11 STL or Boost + * + * + * + * + * Operating System: + * or + * or (for ntohl.. could potentially bundle this) + * + * @since 0.4.0-dev + */ +typedef minimal_server minimal_client; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP diff --git a/subprojects/websocketpp/websocketpp/config/minimal_server.hpp b/subprojects/websocketpp/websocketpp/config/minimal_server.hpp new file mode 100644 index 000000000..dd1aedb9d --- /dev/null +++ b/subprojects/websocketpp/websocketpp/config/minimal_server.hpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_MINIMAL_HPP +#define WEBSOCKETPP_CONFIG_MINIMAL_HPP + +// Non-Policy common stuff +#include +#include +#include + +// Concurrency +#include + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Server config with minimal dependencies +/** + * This config strips out as many dependencies as possible. It is suitable for + * use as a base class for custom configs that want to implement or choose their + * own policies for components that even the core config includes. + * + * NOTE: this config stubs out enough that it cannot be used directly. You must + * supply at least a transport policy for a config based on `minimal_server` to + * do anything useful. + * + * Present dependency list for minimal_server config: + * + * C++98 STL: + * + * + * + * + * + * + * C++11 STL or Boost + * + * + * + * + * Operating System: + * or + * or (for ntohl.. could potentially bundle this) + * + * @since 0.4.0-dev + */ +struct minimal_server { + typedef minimal_server type; + + // Concurrency policy + typedef websocketpp::concurrency::none concurrency_type; + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::stub elog_type; + typedef websocketpp::log::stub alog_type; + + /// RNG policies + typedef websocketpp::random::none::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::stub::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::none; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::none; + + /// + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.4.0-alpha1 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_compress extension + struct permessage_deflate_config { + typedef core::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-deflate + /** + * Automatically enables the permessage-deflate extension. + * + * For clients this results in a permessage-deflate extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_MINIMAL_HPP diff --git a/subprojects/websocketpp/websocketpp/connection.hpp b/subprojects/websocketpp/websocketpp/connection.hpp new file mode 100644 index 000000000..d019fce33 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/connection.hpp @@ -0,0 +1,1642 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONNECTION_HPP +#define WEBSOCKETPP_CONNECTION_HPP + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { + +/// The type and function signature of an open handler +/** + * The open handler is called once for every successful WebSocket connection + * attempt. Either the fail handler or the open handler will be called for each + * WebSocket connection attempt. HTTP Connections that did not attempt to + * upgrade the connection to the WebSocket protocol will trigger the http + * handler instead of fail/open. + */ +typedef lib::function open_handler; + +/// The type and function signature of a close handler +/** + * The close handler is called once for every successfully established + * connection after it is no longer capable of sending or receiving new messages + * + * The close handler will be called exactly once for every connection for which + * the open handler was called. + */ +typedef lib::function close_handler; + +/// The type and function signature of a fail handler +/** + * The fail handler is called once for every unsuccessful WebSocket connection + * attempt. Either the fail handler or the open handler will be called for each + * WebSocket connection attempt. HTTP Connections that did not attempt to + * upgrade the connection to the WebSocket protocol will trigger the http + * handler instead of fail/open. + */ +typedef lib::function fail_handler; + +/// The type and function signature of an interrupt handler +/** + * The interrupt handler is called when a connection receives an interrupt + * request from the application. Interrupts allow the application to trigger a + * handler to be run in the absense of a WebSocket level handler trigger (like + * a new message). + * + * This is typically used by another application thread to schedule some tasks + * that can only be run from within the handler chain for thread safety reasons. + */ +typedef lib::function interrupt_handler; + +/// The type and function signature of a ping handler +/** + * The ping handler is called when the connection receives a WebSocket ping + * control frame. The string argument contains the ping payload. The payload is + * a binary string up to 126 bytes in length. The ping handler returns a bool, + * true if a pong response should be sent, false if the pong response should be + * suppressed. + */ +typedef lib::function ping_handler; + +/// The type and function signature of a pong handler +/** + * The pong handler is called when the connection receives a WebSocket pong + * control frame. The string argument contains the pong payload. The payload is + * a binary string up to 126 bytes in length. + */ +typedef lib::function pong_handler; + +/// The type and function signature of a pong timeout handler +/** + * The pong timeout handler is called when a ping goes unanswered by a pong for + * longer than the locally specified timeout period. + */ +typedef lib::function pong_timeout_handler; + +/// The type and function signature of a validate handler +/** + * The validate handler is called after a WebSocket handshake has been received + * and processed but before it has been accepted. This gives the application a + * chance to implement connection details specific policies for accepting + * connections and the ability to negotiate extensions and subprotocols. + * + * The validate handler return value indicates whether or not the connection + * should be accepted. Additional methods may be called during the function to + * set response headers, set HTTP return/error codes, etc. + */ +typedef lib::function validate_handler; + +/// The type and function signature of a http handler +/** + * The http handler is called when an HTTP connection is made that does not + * attempt to upgrade the connection to the WebSocket protocol. This allows + * WebSocket++ servers to respond to these requests with regular HTTP responses. + * + * This can be used to deliver error pages & dashboards and to deliver static + * files such as the base HTML & JavaScript for an otherwise single page + * WebSocket application. + * + * Note: WebSocket++ is designed to be a high performance WebSocket server. It + * is not tuned to provide a full featured, high performance, HTTP web server + * solution. The HTTP handler is appropriate only for low volume HTTP traffic. + * If you expect to serve high volumes of HTTP traffic a dedicated HTTP web + * server is strongly recommended. + * + * The default HTTP handler will return a 426 Upgrade Required error. Custom + * handlers may override the response status code to deliver any type of + * response. + */ +typedef lib::function http_handler; + +// +typedef lib::function read_handler; +typedef lib::function write_frame_handler; + +// constants related to the default WebSocket protocol versions available +#ifdef _WEBSOCKETPP_INITIALIZER_LISTS_ // simplified C++11 version + /// Container that stores the list of protocol versions supported + /** + * @todo Move this to configs to allow compile/runtime disabling or enabling + * of protocol versions + */ + static std::vector const versions_supported = {0,7,8,13}; +#else + /// Helper array to get around lack of initializer lists pre C++11 + static int const helper[] = {0,7,8,13}; + /// Container that stores the list of protocol versions supported + /** + * @todo Move this to configs to allow compile/runtime disabling or enabling + * of protocol versions + */ + static std::vector const versions_supported(helper,helper+4); +#endif + +namespace session { +namespace state { + // externally visible session state (states based on the RFC) + enum value { + connecting = 0, + open = 1, + closing = 2, + closed = 3 + }; +} // namespace state + + +namespace fail { +namespace status { + enum value { + GOOD = 0, // no failure yet! + SYSTEM = 1, // system call returned error, check that code + WEBSOCKET = 2, // websocket close codes contain error + UNKNOWN = 3, // No failure information is available + TIMEOUT_TLS = 4, // TLS handshake timed out + TIMEOUT_WS = 5 // WS handshake timed out + }; +} // namespace status +} // namespace fail + +namespace internal_state { + // More granular internal states. These are used for multi-threaded + // connection synchronization and preventing values that are not yet or no + // longer available from being used. + + enum value { + USER_INIT = 0, + TRANSPORT_INIT = 1, + READ_HTTP_REQUEST = 2, + WRITE_HTTP_REQUEST = 3, + READ_HTTP_RESPONSE = 4, + WRITE_HTTP_RESPONSE = 5, + PROCESS_HTTP_REQUEST = 6, + PROCESS_CONNECTION = 7 + }; +} // namespace internal_state + + +namespace http_state { + // states to keep track of the progress of http connections + + enum value { + init = 0, + deferred = 1, + headers_written = 2, + body_written = 3, + closed = 4 + }; +} // namespace http_state + +} // namespace session + +/// Represents an individual WebSocket connection +template +class connection + : public config::transport_type::transport_con_type + , public config::connection_base +{ +public: + /// Type of this connection + typedef connection type; + /// Type of a shared pointer to this connection + typedef lib::shared_ptr ptr; + /// Type of a weak pointer to this connection + typedef lib::weak_ptr weak_ptr; + + /// Type of the concurrency component of this connection + typedef typename config::concurrency_type concurrency_type; + /// Type of the access logging policy + typedef typename config::alog_type alog_type; + /// Type of the error logging policy + typedef typename config::elog_type elog_type; + + /// Type of the transport component of this connection + typedef typename config::transport_type::transport_con_type + transport_con_type; + /// Type of a shared pointer to the transport component of this connection + typedef typename transport_con_type::ptr transport_con_ptr; + + typedef lib::function termination_handler; + + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + + typedef typename config::message_type message_type; + typedef typename message_type::ptr message_ptr; + + typedef typename config::con_msg_manager_type con_msg_manager_type; + typedef typename con_msg_manager_type::ptr con_msg_manager_ptr; + + /// Type of RNG + typedef typename config::rng_type rng_type; + + typedef processor::processor processor_type; + typedef lib::shared_ptr processor_ptr; + + // Message handler (needs to know message type) + typedef lib::function message_handler; + + /// Type of a pointer to a transport timer handle + typedef typename transport_con_type::timer_ptr timer_ptr; + + // Misc Convenience Types + typedef session::internal_state::value istate_type; + +private: + enum terminate_status { + failed = 1, + closed, + unknown + }; +public: + + explicit connection(bool p_is_server, std::string const & ua, const lib::shared_ptr& alog, + const lib::shared_ptr& elog, rng_type & rng) + : transport_con_type(p_is_server, alog, elog) + , m_handle_read_frame(lib::bind( + &type::handle_read_frame, + this, + lib::placeholders::_1, + lib::placeholders::_2 + )) + , m_write_frame_handler(lib::bind( + &type::handle_write_frame, + this, + lib::placeholders::_1 + )) + , m_user_agent(ua) + , m_open_handshake_timeout_dur(config::timeout_open_handshake) + , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_pong_timeout_dur(config::timeout_pong) + , m_max_message_size(config::max_message_size) + , m_state(session::state::connecting) + , m_internal_state(session::internal_state::USER_INIT) + , m_msg_manager(new con_msg_manager_type()) + , m_send_buffer_size(0) + , m_write_flag(false) + , m_read_flag(true) + , m_is_server(p_is_server) + , m_alog(alog) + , m_elog(elog) + , m_rng(rng) + , m_local_close_code(close::status::abnormal_close) + , m_remote_close_code(close::status::abnormal_close) + , m_is_http(false) + , m_http_state(session::http_state::init) + , m_was_clean(false) + { + m_alog->write(log::alevel::devel,"connection constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return lib::static_pointer_cast(transport_con_type::get_shared()); + } + + /////////////////////////// + // Set Handler Callbacks // + /////////////////////////// + + /// Set open handler + /** + * The open handler is called after the WebSocket handshake is complete and + * the connection is considered OPEN. + * + * @param h The new open_handler + */ + void set_open_handler(open_handler h) { + m_open_handler = h; + } + + /// Set close handler + /** + * The close handler is called immediately after the connection is closed. + * + * @param h The new close_handler + */ + void set_close_handler(close_handler h) { + m_close_handler = h; + } + + /// Set fail handler + /** + * The fail handler is called whenever the connection fails while the + * handshake is bring processed. + * + * @param h The new fail_handler + */ + void set_fail_handler(fail_handler h) { + m_fail_handler = h; + } + + /// Set ping handler + /** + * The ping handler is called whenever the connection receives a ping + * control frame. The ping payload is included. + * + * The ping handler's return time controls whether or not a pong is + * sent in response to this ping. Returning false will suppress the + * return pong. If no ping handler is set a pong will be sent. + * + * @param h The new ping_handler + */ + void set_ping_handler(ping_handler h) { + m_ping_handler = h; + } + + /// Set pong handler + /** + * The pong handler is called whenever the connection receives a pong + * control frame. The pong payload is included. + * + * @param h The new pong_handler + */ + void set_pong_handler(pong_handler h) { + m_pong_handler = h; + } + + /// Set pong timeout handler + /** + * If the transport component being used supports timers, the pong timeout + * handler is called whenever a pong control frame is not received with the + * configured timeout period after the application sends a ping. + * + * The config setting `timeout_pong` controls the length of the timeout + * period. It is specified in milliseconds. + * + * This can be used to probe the health of the remote endpoint's WebSocket + * implementation. This does not guarantee that the remote application + * itself is still healthy but can be a useful diagnostic. + * + * Note: receipt of this callback doesn't mean the pong will never come. + * This functionality will not suppress delivery of the pong in question + * should it arrive after the timeout. + * + * @param h The new pong_timeout_handler + */ + void set_pong_timeout_handler(pong_timeout_handler h) { + m_pong_timeout_handler = h; + } + + /// Set interrupt handler + /** + * The interrupt handler is called whenever the connection is manually + * interrupted by the application. + * + * @param h The new interrupt_handler + */ + void set_interrupt_handler(interrupt_handler h) { + m_interrupt_handler = h; + } + + /// Set http handler + /** + * The http handler is called after an HTTP request other than a WebSocket + * upgrade request is received. It allows a WebSocket++ server to respond + * to regular HTTP requests on the same port as it processes WebSocket + * connections. This can be useful for hosting error messages, flash + * policy files, status pages, and other simple HTTP responses. It is not + * intended to be used as a primary web server. + * + * @param h The new http_handler + */ + void set_http_handler(http_handler h) { + m_http_handler = h; + } + + /// Set validate handler + /** + * The validate handler is called after a WebSocket handshake has been + * parsed but before a response is returned. It provides the application + * a chance to examine the request and determine whether or not it wants + * to accept the connection. + * + * Returning false from the validate handler will reject the connection. + * If no validate handler is present, all connections will be allowed. + * + * @param h The new validate_handler + */ + void set_validate_handler(validate_handler h) { + m_validate_handler = h; + } + + /// Set message handler + /** + * The message handler is called after a new message has been received. + * + * @param h The new message_handler + */ + void set_message_handler(message_handler h) { + m_message_handler = h; + } + + ////////////////////////////////////////// + // Connection timeouts and other limits // + ////////////////////////////////////////// + + /// Set open handshake timeout + /** + * Sets the length of time the library will wait after an opening handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their fail handlers called with the + * open_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_open_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the open handshake timeout in ms + */ + void set_open_handshake_timeout(long dur) { + m_open_handshake_timeout_dur = dur; + } + + /// Set close handshake timeout + /** + * Sets the length of time the library will wait after a closing handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their close handlers called with the + * close_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_close_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the close handshake timeout in ms + */ + void set_close_handshake_timeout(long dur) { + m_close_handshake_timeout_dur = dur; + } + + /// Set pong timeout + /** + * Sets the length of time the library will wait for a pong response to a + * ping. This can be used as a keepalive or to detect broken connections. + * + * Pong responses that time out will have the pong timeout handler called. + * + * The default value is specified via the compile time config value + * 'timeout_pong'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the pong timeout in ms + */ + void set_pong_timeout(long dur) { + m_pong_timeout_dur = dur; + } + + /// Get maximum message size + /** + * Get maximum message size. Maximum message size determines the point at + * which the connection will fail with the message_too_big protocol error. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.3.0 + */ + size_t get_max_message_size() const { + return m_max_message_size; + } + + /// Set maximum message size + /** + * Set maximum message size. Maximum message size determines the point at + * which the connection will fail with the message_too_big protocol error. + * This value may be changed during the connection. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.3.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_message_size(size_t new_value) { + m_max_message_size = new_value; + if (m_processor) { + m_processor->set_max_message_size(new_value); + } + } + + /// Get maximum HTTP message body size + /** + * Get maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.5.0 + * + * @return The maximum HTTP message body size + */ + size_t get_max_http_body_size() const { + return m_request.get_max_body_size(); + } + + /// Set maximum HTTP message body size + /** + * Set maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.5.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_http_body_size(size_t new_value) { + m_request.set_max_body_size(new_value); + } + + ////////////////////////////////// + // Uncategorized public methods // + ////////////////////////////////// + + /// Get the size of the outgoing write buffer (in payload bytes) + /** + * Retrieves the number of bytes in the outgoing write buffer that have not + * already been dispatched to the transport layer. This represents the bytes + * that are presently cancelable without uncleanly ending the websocket + * connection + * + * This method invokes the m_write_lock mutex + * + * @return The current number of bytes in the outgoing send buffer. + */ + size_t get_buffered_amount() const; + + /// Get the size of the outgoing write buffer (in payload bytes) + /** + * @deprecated use `get_buffered_amount` instead + */ + size_t buffered_amount() const { + return get_buffered_amount(); + } + + //////////////////// + // Action Methods // + //////////////////// + + /// Create a message and then add it to the outgoing send queue + /** + * Convenience method to send a message given a payload string and + * optionally an opcode. Default opcode is utf8 text. + * + * This method locks the m_write_lock mutex + * + * @param payload The payload string to generated the message with + * + * @param op The opcode to generated the message with. Default is + * frame::opcode::text + */ + lib::error_code send(std::string const & payload, frame::opcode::value op = + frame::opcode::text); + + /// Send a message (raw array overload) + /** + * Convenience method to send a message given a raw array and optionally an + * opcode. Default opcode is binary. + * + * This method locks the m_write_lock mutex + * + * @param payload A pointer to the array containing the bytes to send. + * + * @param len Length of the array. + * + * @param op The opcode to generated the message with. Default is + * frame::opcode::binary + */ + lib::error_code send(void const * payload, size_t len, frame::opcode::value + op = frame::opcode::binary); + + /// Add a message to the outgoing send queue + /** + * If presented with a prepared message it is added without validation or + * framing. If presented with an unprepared message it is validated, framed, + * and then added + * + * Errors are returned via an exception + * \todo make exception system_error rather than error_code + * + * This method invokes the m_write_lock mutex + * + * @param msg A message_ptr to the message to send. + */ + lib::error_code send(message_ptr msg); + + /// Asyncronously invoke handler::on_inturrupt + /** + * Signals to the connection to asyncronously invoke the on_inturrupt + * callback for this connection's handler once it is safe to do so. + * + * When the on_inturrupt handler callback is called it will be from + * within the transport event loop with all the thread safety features + * guaranteed by the transport to regular handlers + * + * Multiple inturrupt signals can be active at once on the same connection + * + * @return An error code + */ + lib::error_code interrupt(); + + /// Transport inturrupt callback + void handle_interrupt(); + + /// Pause reading of new data + /** + * Signals to the connection to halt reading of new data. While reading is paused, + * the connection will stop reading from its associated socket. In turn this will + * result in TCP based flow control kicking in and slowing data flow from the remote + * endpoint. + * + * This is useful for applications that push new requests to a queue to be processed + * by another thread and need a way to signal when their request queue is full without + * blocking the network processing thread. + * + * Use `resume_reading()` to resume. + * + * If supported by the transport this is done asynchronously. As such reading may not + * stop until the current read operation completes. Typically you can expect to + * receive no more bytes after initiating a read pause than the size of the read + * buffer. + * + * If reading is paused for this connection already nothing is changed. + */ + lib::error_code pause_reading(); + + /// Pause reading callback + void handle_pause_reading(); + + /// Resume reading of new data + /** + * Signals to the connection to resume reading of new data after it was paused by + * `pause_reading()`. + * + * If reading is not paused for this connection already nothing is changed. + */ + lib::error_code resume_reading(); + + /// Resume reading callback + void handle_resume_reading(); + + /// Send a ping + /** + * Initiates a ping with the given payload/ + * + * There is no feedback directly from ping except in cases of immediately + * detectable errors. Feedback will be provided via on_pong or + * on_pong_timeout callbacks. + * + * Ping locks the m_write_lock mutex + * + * @param payload Payload to be used for the ping + */ + void ping(std::string const & payload); + + /// exception free variant of ping + void ping(std::string const & payload, lib::error_code & ec); + + /// Utility method that gets called back when the ping timer expires + void handle_pong_timeout(std::string payload, lib::error_code const & ec); + + /// Send a pong + /** + * Initiates a pong with the given payload. + * + * There is no feedback from a pong once sent. + * + * Pong locks the m_write_lock mutex + * + * @param payload Payload to be used for the pong + */ + void pong(std::string const & payload); + + /// exception free variant of pong + void pong(std::string const & payload, lib::error_code & ec); + + /// Close the connection + /** + * Initiates the close handshake process. + * + * If close returns successfully the connection will be in the closing + * state and no additional messages may be sent. All messages sent prior + * to calling close will be written out before the connection is closed. + * + * If no reason is specified none will be sent. If no code is specified + * then no code will be sent. + * + * The handler's on_close callback will be called once the close handshake + * is complete. + * + * Reasons will be automatically truncated to the maximum length (123 bytes) + * if necessary. + * + * @param code The close code to send + * @param reason The close reason to send + */ + void close(close::status::value const code, std::string const & reason); + + /// exception free variant of close + void close(close::status::value const code, std::string const & reason, + lib::error_code & ec); + + //////////////////////////////////////////////// + // Pass-through access to the uri information // + //////////////////////////////////////////////// + + /// Returns the secure flag from the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return Whether or not the connection URI is flagged secure. + */ + bool get_secure() const; + + /// Returns the host component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The host component of the connection URI + */ + std::string const & get_host() const; + + /// Returns the resource component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The resource component of the connection URI + */ + std::string const & get_resource() const; + + /// Returns the port component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The port component of the connection URI + */ + uint16_t get_port() const; + + /// Gets the connection URI + /** + * This should really only be called by internal library methods unless you + * really know what you are doing. + * + * @return A pointer to the connection's URI + */ + uri_ptr get_uri() const; + + /// Sets the connection URI + /** + * This should really only be called by internal library methods unless you + * really know what you are doing. + * + * @param uri The new URI to set + */ + void set_uri(uri_ptr uri); + + ///////////////////////////// + // Subprotocol negotiation // + ///////////////////////////// + + /// Gets the negotated subprotocol + /** + * Retrieves the subprotocol that was negotiated during the handshake. This + * method is valid in the open handler and later. + * + * @return The negotiated subprotocol + */ + std::string const & get_subprotocol() const; + + /// Gets all of the subprotocols requested by the client + /** + * Retrieves the subprotocols that were requested during the handshake. This + * method is valid in the validate handler and later. + * + * @return A vector of the requested subprotocol + */ + std::vector const & get_requested_subprotocols() const; + + /// Adds the given subprotocol string to the request list (exception free) + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + * @param ec A reference to an error code that will be filled in the case of + * errors + */ + void add_subprotocol(std::string const & request, lib::error_code & ec); + + /// Adds the given subprotocol string to the request list + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + */ + void add_subprotocol(std::string const & request); + + /// Select a subprotocol to use (exception free) + /** + * Indicates which subprotocol should be used for this connection. Valid + * only during the validate handler callback. Subprotocol selected must have + * been requested by the client. Consult get_requested_subprotocols() for a + * list of valid subprotocols. + * + * This member function is valid on server endpoints/connections only + * + * @param value The subprotocol to select + * @param ec A reference to an error code that will be filled in the case of + * errors + */ + void select_subprotocol(std::string const & value, lib::error_code & ec); + + /// Select a subprotocol to use + /** + * Indicates which subprotocol should be used for this connection. Valid + * only during the validate handler callback. Subprotocol selected must have + * been requested by the client. Consult get_requested_subprotocols() for a + * list of valid subprotocols. + * + * This member function is valid on server endpoints/connections only + * + * @param value The subprotocol to select + */ + void select_subprotocol(std::string const & value); + + ///////////////////////////////////////////////////////////// + // Pass-through access to the request and response objects // + ///////////////////////////////////////////////////////////// + + /// Retrieve a request header + /** + * Retrieve the value of a header from the handshake HTTP request. + * + * @param key Name of the header to get + * @return The value of the header + */ + std::string const & get_request_header(std::string const & key) const; + + /// Retrieve a request body + /** + * Retrieve the value of the request body. This value is typically used with + * PUT and POST requests to upload files or other data. Only HTTP + * connections will ever have bodies. WebSocket connection's will always + * have blank bodies. + * + * @return The value of the request body. + */ + std::string const & get_request_body() const; + + /// Retrieve a response header + /** + * Retrieve the value of a header from the handshake HTTP request. + * + * @param key Name of the header to get + * @return The value of the header + */ + std::string const & get_response_header(std::string const & key) const; + + /// Get response HTTP status code + /** + * Gets the response status code + * + * @since 0.7.0 + * + * @return The response status code sent + */ + http::status_code::value get_response_code() const { + return m_response.get_status_code(); + } + + /// Get response HTTP status message + /** + * Gets the response status message + * + * @since 0.7.0 + * + * @return The response status message sent + */ + std::string const & get_response_msg() const { + return m_response.get_status_msg(); + } + + /// Set response status code and message + /** + * Sets the response status code to `code` and looks up the corresponding + * message for standard codes. Non-standard codes will be entered as Unknown + * use set_status(status_code::value,std::string) overload to set both + * values explicitly. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param code Code to set + * @param msg Message to set + * @see websocketpp::http::response::set_status + */ + void set_status(http::status_code::value code); + + /// Set response status code and message + /** + * Sets the response status code and message to independent custom values. + * use set_status(status_code::value) to set the code and have the standard + * message be automatically set. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param code Code to set + * @param msg Message to set + * @see websocketpp::http::response::set_status + */ + void set_status(http::status_code::value code, std::string const & msg); + + /// Set response body content + /** + * Set the body content of the HTTP response to the parameter string. Note + * set_body will also set the Content-Length HTTP header to the appropriate + * value. If you want the Content-Length header to be something else set it + * to something else after calling set_body + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param value String data to include as the body content. + * @see websocketpp::http::response::set_body + */ + void set_body(std::string const & value); + + /// Append a header + /** + * If a header with this name already exists the value will be appended to + * the existing header to form a comma separated list of values. Use + * `connection::replace_header` to overwrite existing values. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key Name of the header to set + * @param val Value to add + * @see replace_header + * @see websocketpp::http::parser::append_header + */ + void append_header(std::string const & key, std::string const & val); + + /// Replace a header + /** + * If a header with this name already exists the old value will be replaced + * Use `connection::append_header` to append to a list of existing values. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key Name of the header to set + * @param val Value to set + * @see append_header + * @see websocketpp::http::parser::replace_header + */ + void replace_header(std::string const & key, std::string const & val); + + /// Remove a header + /** + * Removes a header from the response. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key The name of the header to remove + * @see websocketpp::http::parser::remove_header + */ + void remove_header(std::string const & key); + + /// Get request object + /** + * Direct access to request object. This can be used to call methods of the + * request object that are not part of the standard request API that + * connection wraps. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.3.0-alpha3 + * + * @return A const reference to the raw request object + */ + request_type const & get_request() const { + return m_request; + } + + /// Get response object + /** + * Direct access to the HTTP response sent or received as a part of the + * opening handshake. This can be used to call methods of the response + * object that are not part of the standard request API that connection + * wraps. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.7.0 + * + * @return A const reference to the raw response object + */ + response_type const & get_response() const { + return m_response; + } + + /// Defer HTTP Response until later (Exception free) + /** + * Used in the http handler to defer the HTTP response for this connection + * until later. Handshake timers will be canceled and the connection will be + * left open until `send_http_response` or an equivalent is called. + * + * Warning: deferred connections won't time out and as a result can tie up + * resources. + * + * @since 0.6.0 + * + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code defer_http_response(); + + /// Send deferred HTTP Response (exception free) + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * @since 0.6.0 + * + * @param ec A status code, zero on success, non-zero otherwise + */ + void send_http_response(lib::error_code & ec); + + /// Send deferred HTTP Response + void send_http_response(); + + // TODO HTTPNBIO: write_headers + // function that processes headers + status so far and writes it to the wire + // beginning the HTTP response body state. This method will ignore anything + // in the response body. + + // TODO HTTPNBIO: write_body_message + // queues the specified message_buffer for async writing + + // TODO HTTPNBIO: finish connection + // + + // TODO HTTPNBIO: write_response + // Writes the whole response, headers + body and closes the connection + + + + ///////////////////////////////////////////////////////////// + // Pass-through access to the other connection information // + ///////////////////////////////////////////////////////////// + + /// Get Connection Handle + /** + * The connection handle is a token that can be shared outside the + * WebSocket++ core for the purposes of identifying a connection and + * sending it messages. + * + * @return A handle to the connection + */ + connection_hdl get_handle() const { + return m_connection_hdl; + } + + /// Get whether or not this connection is part of a server or client + /** + * @return whether or not the connection is attached to a server endpoint + */ + bool is_server() const { + return m_is_server; + } + + /// Return the same origin policy origin value from the opening request. + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The connection's origin value from the opening handshake. + */ + std::string const & get_origin() const; + + /// Return the connection state. + /** + * Values can be connecting, open, closing, and closed + * + * @return The connection's current state. + */ + session::state::value get_state() const; + + + /// Get the WebSocket close code sent by this endpoint. + /** + * @return The WebSocket close code sent by this endpoint. + */ + close::status::value get_local_close_code() const { + return m_local_close_code; + } + + /// Get the WebSocket close reason sent by this endpoint. + /** + * @return The WebSocket close reason sent by this endpoint. + */ + std::string const & get_local_close_reason() const { + return m_local_close_reason; + } + + /// Get the WebSocket close code sent by the remote endpoint. + /** + * @return The WebSocket close code sent by the remote endpoint. + */ + close::status::value get_remote_close_code() const { + return m_remote_close_code; + } + + /// Get the WebSocket close reason sent by the remote endpoint. + /** + * @return The WebSocket close reason sent by the remote endpoint. + */ + std::string const & get_remote_close_reason() const { + return m_remote_close_reason; + } + + /// Get the internal error code for a closed/failed connection + /** + * Retrieves a machine readable detailed error code indicating the reason + * that the connection was closed or failed. Valid only after the close or + * fail handler is called. + * + * @return Error code indicating the reason the connection was closed or + * failed + */ + lib::error_code get_ec() const { + return m_ec; + } + + /// Get a message buffer + /** + * Warning: The API related to directly sending message buffers may change + * before the 1.0 release. If you plan to use it, please keep an eye on any + * breaking changes notifications in future release notes. Also if you have + * any feedback about usage and capabilities now is a great time to provide + * it. + * + * Message buffers are used to store message payloads and other message + * metadata. + * + * The size parameter is a hint only. Your final payload does not need to + * match it. There may be some performance benefits if the initial size + * guess is equal to or slightly higher than the final payload size. + * + * @param op The opcode for the new message + * @param size A hint to optimize the initial allocation of payload space. + * @return A new message buffer + */ + message_ptr get_message(websocketpp::frame::opcode::value op, size_t size) + const + { + return m_msg_manager->get_message(op, size); + } + + //////////////////////////////////////////////////////////////////////// + // The remaining public member functions are for internal/policy use // + // only. Do not call from application code unless you understand what // + // you are doing. // + //////////////////////////////////////////////////////////////////////// + + + + void read_handshake(size_t num_bytes); + + void handle_read_handshake(lib::error_code const & ec, + size_t bytes_transferred); + void handle_read_http_response(lib::error_code const & ec, + size_t bytes_transferred); + + + void handle_write_http_response(lib::error_code const & ec); + void handle_send_http_request(lib::error_code const & ec); + + void handle_open_handshake_timeout(lib::error_code const & ec); + void handle_close_handshake_timeout(lib::error_code const & ec); + + void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred); + void read_frame(); + + /// Get array of WebSocket protocol versions that this connection supports. + std::vector const & get_supported_versions() const; + + /// Sets the handler for a terminating connection. Should only be used + /// internally by the endpoint class. + void set_termination_handler(termination_handler new_handler); + + void terminate(lib::error_code const & ec); + void handle_terminate(terminate_status tstat, lib::error_code const & ec); + + /// Checks if there are frames in the send queue and if there are sends one + /** + * \todo unit tests + * + * This method locks the m_write_lock mutex + */ + void write_frame(); + + /// Process the results of a frame write operation and start the next write + /** + * \todo unit tests + * + * This method locks the m_write_lock mutex + * + * @param terminate Whether or not to terminate the connection upon + * completion of this write. + * + * @param ec A status code from the transport layer, zero on success, + * non-zero otherwise. + */ + void handle_write_frame(lib::error_code const & ec); +// protected: + // This set of methods would really like to be protected, but doing so + // requires that the endpoint be able to friend the connection. This is + // allowed with C++11, but not prior versions + + /// Start the connection state machine + void start(); + + /// Set Connection Handle + /** + * The connection handle is a token that can be shared outside the + * WebSocket++ core for the purposes of identifying a connection and + * sending it messages. + * + * @param hdl A connection_hdl that the connection will use to refer + * to itself. + */ + void set_handle(connection_hdl hdl) { + m_connection_hdl = hdl; + transport_con_type::set_handle(hdl); + } +protected: + void handle_transport_init(lib::error_code const & ec); + + /// Set m_processor based on information in m_request. Set m_response + /// status and return an error code indicating status. + lib::error_code initialize_processor(); + + /// Perform WebSocket handshake validation of m_request using m_processor. + /// set m_response and return an error code indicating status. + lib::error_code process_handshake_request(); +private: + + + /// Completes m_response, serializes it, and sends it out on the wire. + void write_http_response(lib::error_code const & ec); + + /// Sends an opening WebSocket connect request + void send_http_request(); + + /// Alternate path for write_http_response in error conditions + void write_http_response_error(lib::error_code const & ec); + + /// Process control message + /** + * + */ + void process_control_frame(message_ptr msg); + + /// Send close acknowledgement + /** + * If no arguments are present no close code/reason will be specified. + * + * Note: the close code/reason values provided here may be overrided by + * other settings (such as silent close). + * + * @param code The close code to send + * @param reason The close reason to send + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code send_close_ack(close::status::value code = + close::status::blank, std::string const & reason = std::string()); + + /// Send close frame + /** + * If no arguments are present no close code/reason will be specified. + * + * Note: the close code/reason values provided here may be overrided by + * other settings (such as silent close). + * + * The ack flag determines what to do in the case of a blank status and + * whether or not to terminate the TCP connection after sending it. + * + * @param code The close code to send + * @param reason The close reason to send + * @param ack Whether or not this is an acknowledgement close frame + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code send_close_frame(close::status::value code = + close::status::blank, std::string const & reason = std::string(), bool ack = false, + bool terminal = false); + + /// Get a pointer to a new WebSocket protocol processor for a given version + /** + * @param version Version number of the WebSocket protocol to get a + * processor for. Negative values indicate invalid/unknown versions and will + * always return a null ptr + * + * @return A shared_ptr to a new instance of the appropriate processor or a + * null ptr if there is no installed processor that matches the version + * number. + */ + processor_ptr get_processor(int version) const; + + /// Add a message to the write queue + /** + * Adds a message to the write queue and updates any associated shared state + * + * Must be called while holding m_write_lock + * + * @todo unit tests + * + * @param msg The message to push + */ + void write_push(message_ptr msg); + + /// Pop a message from the write queue + /** + * Removes and returns a message from the write queue and updates any + * associated shared state. + * + * Must be called while holding m_write_lock + * + * @todo unit tests + * + * @return the message_ptr at the front of the queue + */ + message_ptr write_pop(); + + /// Prints information about the incoming connection to the access log + /** + * Prints information about the incoming connection to the access log. + * Includes: connection type, websocket version, remote endpoint, user agent + * path, status code. + */ + void log_open_result(); + + /// Prints information about a connection being closed to the access log + /** + * Includes: local and remote close codes and reasons + */ + void log_close_result(); + + /// Prints information about a connection being failed to the access log + /** + * Includes: error code and message for why it was failed + */ + void log_fail_result(); + + /// Prints information about HTTP connections + /** + * Includes: TODO + */ + void log_http_result(); + + /// Prints information about an arbitrary error code on the specified channel + template + void log_err(log::level l, char const * msg, error_type const & ec) { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog->write(l, s.str()); + } + + // internal handler functions + read_handler m_handle_read_frame; + write_frame_handler m_write_frame_handler; + + // static settings + std::string const m_user_agent; + + /// Pointer to the connection handle + connection_hdl m_connection_hdl; + + /// Handler objects + open_handler m_open_handler; + close_handler m_close_handler; + fail_handler m_fail_handler; + ping_handler m_ping_handler; + pong_handler m_pong_handler; + pong_timeout_handler m_pong_timeout_handler; + interrupt_handler m_interrupt_handler; + http_handler m_http_handler; + validate_handler m_validate_handler; + message_handler m_message_handler; + + /// constant values + long m_open_handshake_timeout_dur; + long m_close_handshake_timeout_dur; + long m_pong_timeout_dur; + size_t m_max_message_size; + + /// External connection state + /** + * Lock: m_connection_state_lock + */ + session::state::value m_state; + + /// Internal connection state + /** + * Lock: m_connection_state_lock + */ + istate_type m_internal_state; + + mutable mutex_type m_connection_state_lock; + + /// The lock used to protect the message queue + /** + * Serializes access to the write queue as well as shared state within the + * processor. + */ + mutex_type m_write_lock; + + // connection resources + char m_buf[config::connection_read_buffer_size]; + size_t m_buf_cursor; + termination_handler m_termination_handler; + con_msg_manager_ptr m_msg_manager; + timer_ptr m_handshake_timer; + timer_ptr m_ping_timer; + + /// @todo this is not memory efficient. this value is not used after the + /// handshake. + std::string m_handshake_buffer; + + /// Pointer to the processor object for this connection + /** + * The processor provides functionality that is specific to the WebSocket + * protocol version that the client has negotiated. It also contains all of + * the state necessary to encode and decode the incoming and outgoing + * WebSocket byte streams + * + * Use of the prepare_data_frame method requires lock: m_write_lock + */ + processor_ptr m_processor; + + /// Queue of unsent outgoing messages + /** + * Lock: m_write_lock + */ + std::queue m_send_queue; + + /// Size in bytes of the outstanding payloads in the write queue + /** + * Lock: m_write_lock + */ + size_t m_send_buffer_size; + + /// buffer holding the various parts of the current message being writen + /** + * Lock m_write_lock + */ + std::vector m_send_buffer; + + /// a list of pointers to hold on to the messages being written to keep them + /// from going out of scope before the write is complete. + std::vector m_current_msgs; + + /// True if there is currently an outstanding transport write + /** + * Lock m_write_lock + */ + bool m_write_flag; + + /// True if this connection is presently reading new data + bool m_read_flag; + + // connection data + request_type m_request; + response_type m_response; + uri_ptr m_uri; + std::string m_subprotocol; + + // connection data that might not be necessary to keep around for the life + // of the whole connection. + std::vector m_requested_subprotocols; + + bool const m_is_server; + const lib::shared_ptr m_alog; + const lib::shared_ptr m_elog; + + rng_type & m_rng; + + // Close state + /// Close code that was sent on the wire by this endpoint + close::status::value m_local_close_code; + + /// Close reason that was sent on the wire by this endpoint + std::string m_local_close_reason; + + /// Close code that was received on the wire from the remote endpoint + close::status::value m_remote_close_code; + + /// Close reason that was received on the wire from the remote endpoint + std::string m_remote_close_reason; + + /// Detailed internal error code + lib::error_code m_ec; + + /// A flag that gets set once it is determined that the connection is an + /// HTTP connection and not a WebSocket one. + bool m_is_http; + + /// A flag that gets set when the completion of an http connection is + /// deferred until later. + session::http_state::value m_http_state; + + bool m_was_clean; +}; + +} // namespace websocketpp + +#include + +#endif // WEBSOCKETPP_CONNECTION_HPP diff --git a/subprojects/websocketpp/websocketpp/connection_base.hpp b/subprojects/websocketpp/websocketpp/connection_base.hpp new file mode 100644 index 000000000..2e700962f --- /dev/null +++ b/subprojects/websocketpp/websocketpp/connection_base.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONNECTION_BASE_HPP +#define WEBSOCKETPP_CONNECTION_BASE_HPP + +namespace websocketpp { + +/// Stub for user supplied base class. +class connection_base {}; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONNECTION_BASE_HPP diff --git a/subprojects/websocketpp/websocketpp/endpoint.hpp b/subprojects/websocketpp/websocketpp/endpoint.hpp new file mode 100644 index 000000000..c124b1d9a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/endpoint.hpp @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ENDPOINT_HPP +#define WEBSOCKETPP_ENDPOINT_HPP + +#include + +#include +#include + +#include + +namespace websocketpp { + +/// Creates and manages connections associated with a WebSocket endpoint +template +class endpoint : public config::transport_type, public config::endpoint_base { +public: + // Import appropriate types from our helper class + // See endpoint_types for more details. + typedef endpoint type; + + /// Type of the transport component of this endpoint + typedef typename config::transport_type transport_type; + /// Type of the concurrency component of this endpoint + typedef typename config::concurrency_type concurrency_type; + + /// Type of the connections that this endpoint creates + typedef connection connection_type; + /// Shared pointer to connection_type + typedef typename connection_type::ptr connection_ptr; + /// Weak pointer to connection type + typedef typename connection_type::weak_ptr connection_weak_ptr; + + /// Type of the transport component of the connections that this endpoint + /// creates + typedef typename transport_type::transport_con_type transport_con_type; + /// Type of a shared pointer to the transport component of the connections + /// that this endpoint creates. + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of message_handler + typedef typename connection_type::message_handler message_handler; + /// Type of message pointers that this endpoint uses + typedef typename connection_type::message_ptr message_ptr; + + /// Type of error logger + typedef typename config::elog_type elog_type; + /// Type of access logger + typedef typename config::alog_type alog_type; + + /// Type of our concurrency policy's scoped lock object + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + /// Type of our concurrency policy's mutex object + typedef typename concurrency_type::mutex_type mutex_type; + + /// Type of RNG + typedef typename config::rng_type rng_type; + + // TODO: organize these + typedef typename connection_type::termination_handler termination_handler; + + // This would be ideal. Requires C++11 though + //friend connection; + + explicit endpoint(bool p_is_server) + : m_alog(new alog_type(config::alog_level, log::channel_type_hint::access)) + , m_elog(new elog_type(config::elog_level, log::channel_type_hint::error)) + , m_user_agent(::websocketpp::user_agent) + , m_open_handshake_timeout_dur(config::timeout_open_handshake) + , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_pong_timeout_dur(config::timeout_pong) + , m_max_message_size(config::max_message_size) + , m_max_http_body_size(config::max_http_body_size) + , m_is_server(p_is_server) + { + m_alog->set_channels(config::alog_level); + m_elog->set_channels(config::elog_level); + + m_alog->write(log::alevel::devel, "endpoint constructor"); + + transport_type::init_logging(m_alog, m_elog); + } + + + /// Destructor + ~endpoint() {} + + #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy constructor because endpoints are not copyable + endpoint(endpoint &) = delete; + + // no copy assignment operator because endpoints are not copyable + endpoint & operator=(endpoint const &) = delete; + #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + + #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + endpoint(endpoint && o) + : config::transport_type(std::move(o)) + , config::endpoint_base(std::move(o)) + , m_alog(std::move(o.m_alog)) + , m_elog(std::move(o.m_elog)) + , m_user_agent(std::move(o.m_user_agent)) + , m_open_handler(std::move(o.m_open_handler)) + + , m_close_handler(std::move(o.m_close_handler)) + , m_fail_handler(std::move(o.m_fail_handler)) + , m_ping_handler(std::move(o.m_ping_handler)) + , m_pong_handler(std::move(o.m_pong_handler)) + , m_pong_timeout_handler(std::move(o.m_pong_timeout_handler)) + , m_interrupt_handler(std::move(o.m_interrupt_handler)) + , m_http_handler(std::move(o.m_http_handler)) + , m_validate_handler(std::move(o.m_validate_handler)) + , m_message_handler(std::move(o.m_message_handler)) + + , m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur) + , m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur) + , m_pong_timeout_dur(o.m_pong_timeout_dur) + , m_max_message_size(o.m_max_message_size) + , m_max_http_body_size(o.m_max_http_body_size) + + , m_rng(std::move(o.m_rng)) + , m_is_server(o.m_is_server) + {} + + #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + endpoint & operator=(endpoint &&) = delete; + #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + + #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + + /// Returns the user agent string that this endpoint will use + /** + * Returns the user agent string that this endpoint will use when creating + * new connections. + * + * The default value for this version is stored in websocketpp::user_agent + * + * @return The user agent string. + */ + std::string get_user_agent() const { + scoped_lock_type guard(m_mutex); + return m_user_agent; + } + + /// Sets the user agent string that this endpoint will use + /** + * Sets the identifier that this endpoint will use when creating new + * connections. Changing this value will only affect future connections. + * For client endpoints this will be sent as the "User-Agent" header in + * outgoing requests. For server endpoints this will be sent in the "Server" + * response header. + * + * Setting this value to the empty string will suppress the use of the + * Server and User-Agent headers. This is typically done to hide + * implementation details for security purposes. + * + * For best results set this before accepting or opening connections. + * + * The default value for this version is stored in websocketpp::user_agent + * + * This can be overridden on an individual connection basis by setting a + * custom "Server" header during the validate handler or "User-Agent" + * header on a connection before calling connect(). + * + * @param ua The string to set the user agent to. + */ + void set_user_agent(std::string const & ua) { + scoped_lock_type guard(m_mutex); + m_user_agent = ua; + } + + /// Returns whether or not this endpoint is a server. + /** + * @return Whether or not this endpoint is a server + */ + bool is_server() const { + return m_is_server; + } + + /********************************/ + /* Pass-through logging adaptor */ + /********************************/ + + /// Set Access logging channel + /** + * Set the access logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to set + */ + void set_access_channels(log::level channels) { + m_alog->set_channels(channels); + } + + /// Clear Access logging channels + /** + * Clear the access logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to clear + */ + void clear_access_channels(log::level channels) { + m_alog->clear_channels(channels); + } + + /// Set Error logging channel + /** + * Set the error logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to set + */ + void set_error_channels(log::level channels) { + m_elog->set_channels(channels); + } + + /// Clear Error logging channels + /** + * Clear the error logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to clear + */ + void clear_error_channels(log::level channels) { + m_elog->clear_channels(channels); + } + + /// Get reference to access logger + /** + * @return A reference to the access logger + */ + alog_type & get_alog() { + return *m_alog; + } + + /// Get reference to error logger + /** + * @return A reference to the error logger + */ + elog_type & get_elog() { + return *m_elog; + } + + /*************************/ + /* Set Handler functions */ + /*************************/ + + void set_open_handler(open_handler h) { + m_alog->write(log::alevel::devel,"set_open_handler"); + scoped_lock_type guard(m_mutex); + m_open_handler = h; + } + void set_close_handler(close_handler h) { + m_alog->write(log::alevel::devel,"set_close_handler"); + scoped_lock_type guard(m_mutex); + m_close_handler = h; + } + void set_fail_handler(fail_handler h) { + m_alog->write(log::alevel::devel,"set_fail_handler"); + scoped_lock_type guard(m_mutex); + m_fail_handler = h; + } + void set_ping_handler(ping_handler h) { + m_alog->write(log::alevel::devel,"set_ping_handler"); + scoped_lock_type guard(m_mutex); + m_ping_handler = h; + } + void set_pong_handler(pong_handler h) { + m_alog->write(log::alevel::devel,"set_pong_handler"); + scoped_lock_type guard(m_mutex); + m_pong_handler = h; + } + void set_pong_timeout_handler(pong_timeout_handler h) { + m_alog->write(log::alevel::devel,"set_pong_timeout_handler"); + scoped_lock_type guard(m_mutex); + m_pong_timeout_handler = h; + } + void set_interrupt_handler(interrupt_handler h) { + m_alog->write(log::alevel::devel,"set_interrupt_handler"); + scoped_lock_type guard(m_mutex); + m_interrupt_handler = h; + } + void set_http_handler(http_handler h) { + m_alog->write(log::alevel::devel,"set_http_handler"); + scoped_lock_type guard(m_mutex); + m_http_handler = h; + } + void set_validate_handler(validate_handler h) { + m_alog->write(log::alevel::devel,"set_validate_handler"); + scoped_lock_type guard(m_mutex); + m_validate_handler = h; + } + void set_message_handler(message_handler h) { + m_alog->write(log::alevel::devel,"set_message_handler"); + scoped_lock_type guard(m_mutex); + m_message_handler = h; + } + + ////////////////////////////////////////// + // Connection timeouts and other limits // + ////////////////////////////////////////// + + /// Set open handshake timeout + /** + * Sets the length of time the library will wait after an opening handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their fail handlers called with the + * open_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_open_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the open handshake timeout in ms + */ + void set_open_handshake_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_open_handshake_timeout_dur = dur; + } + + /// Set close handshake timeout + /** + * Sets the length of time the library will wait after a closing handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their close handlers called with the + * close_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_close_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the close handshake timeout in ms + */ + void set_close_handshake_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_close_handshake_timeout_dur = dur; + } + + /// Set pong timeout + /** + * Sets the length of time the library will wait for a pong response to a + * ping. This can be used as a keepalive or to detect broken connections. + * + * Pong responses that time out will have the pong timeout handler called. + * + * The default value is specified via the compile time config value + * 'timeout_pong'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the pong timeout in ms + */ + void set_pong_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_pong_timeout_dur = dur; + } + + /// Get default maximum message size + /** + * Get the default maximum message size that will be used for new + * connections created by this endpoint. The maximum message size determines + * the point at which the connection will fail a connection with the + * message_too_big protocol error. + * + * The default is set by the max_message_size value from the template config + * + * @since 0.3.0 + */ + size_t get_max_message_size() const { + return m_max_message_size; + } + + /// Set default maximum message size + /** + * Set the default maximum message size that will be used for new + * connections created by this endpoint. Maximum message size determines the + * point at which the connection will fail a connection with the + * message_too_big protocol error. + * + * The default is set by the max_message_size value from the template config + * + * @since 0.3.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_message_size(size_t new_value) { + m_max_message_size = new_value; + } + + /// Get maximum HTTP message body size + /** + * Get maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the max_http_body_size value from the template + * config + * + * @since 0.5.0 + * + * @return The maximum HTTP message body size + */ + size_t get_max_http_body_size() const { + return m_max_http_body_size; + } + + /// Set maximum HTTP message body size + /** + * Set maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the max_http_body_size value from the template + * config + * + * @since 0.5.1 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_http_body_size(size_t new_value) { + m_max_http_body_size = new_value; + } + + /*************************************/ + /* Connection pass through functions */ + /*************************************/ + + /** + * These functions act as adaptors to their counterparts in connection. They + * can produce one additional type of error, the bad_connection error, that + * indicates that the conversion from connection_hdl to connection_ptr + * failed due to the connection not existing anymore. Each method has a + * default and an exception free varient. + */ + + void interrupt(connection_hdl hdl, lib::error_code & ec); + void interrupt(connection_hdl hdl); + + /// Pause reading of new data (exception free) + /** + * Signals to the connection to halt reading of new data. While reading is + * paused, the connection will stop reading from its associated socket. In + * turn this will result in TCP based flow control kicking in and slowing + * data flow from the remote endpoint. + * + * This is useful for applications that push new requests to a queue to be + * processed by another thread and need a way to signal when their request + * queue is full without blocking the network processing thread. + * + * Use `resume_reading()` to resume. + * + * If supported by the transport this is done asynchronously. As such + * reading may not stop until the current read operation completes. + * Typically you can expect to receive no more bytes after initiating a read + * pause than the size of the read buffer. + * + * If reading is paused for this connection already nothing is changed. + */ + void pause_reading(connection_hdl hdl, lib::error_code & ec); + + /// Pause reading of new data + void pause_reading(connection_hdl hdl); + + /// Resume reading of new data (exception free) + /** + * Signals to the connection to resume reading of new data after it was + * paused by `pause_reading()`. + * + * If reading is not paused for this connection already nothing is changed. + */ + void resume_reading(connection_hdl hdl, lib::error_code & ec); + + /// Resume reading of new data + void resume_reading(connection_hdl hdl); + + /// Send deferred HTTP Response + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * Exception free variant + * + * @since 0.6.0 + * + * @param hdl The connection to send the response on + * @param ec A status code, zero on success, non-zero otherwise + */ + void send_http_response(connection_hdl hdl, lib::error_code & ec); + + /// Send deferred HTTP Response (exception free) + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * Exception variant + * + * @since 0.6.0 + * + * @param hdl The connection to send the response on + */ + void send_http_response(connection_hdl hdl); + + /// Create a message and add it to the outgoing send queue (exception free) + /** + * Convenience method to send a message given a payload string and an opcode + * + * @param [in] hdl The handle identifying the connection to send via. + * @param [in] payload The payload string to generated the message with + * @param [in] op The opcode to generated the message with. + * @param [out] ec A code to fill in for errors + */ + void send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op, lib::error_code & ec); + /// Create a message and add it to the outgoing send queue + /** + * Convenience method to send a message given a payload string and an opcode + * + * @param [in] hdl The handle identifying the connection to send via. + * @param [in] payload The payload string to generated the message with + * @param [in] op The opcode to generated the message with. + * @param [out] ec A code to fill in for errors + */ + void send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op); + + void send(connection_hdl hdl, void const * payload, size_t len, + frame::opcode::value op, lib::error_code & ec); + void send(connection_hdl hdl, void const * payload, size_t len, + frame::opcode::value op); + + void send(connection_hdl hdl, message_ptr msg, lib::error_code & ec); + void send(connection_hdl hdl, message_ptr msg); + + void close(connection_hdl hdl, close::status::value const code, + std::string const & reason, lib::error_code & ec); + void close(connection_hdl hdl, close::status::value const code, + std::string const & reason); + + /// Send a ping to a specific connection + /** + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + * @param [out] ec A reference to an error code to fill in + */ + void ping(connection_hdl hdl, std::string const & payload, + lib::error_code & ec); + /// Send a ping to a specific connection + /** + * Exception variant of `ping` + * + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + */ + void ping(connection_hdl hdl, std::string const & payload); + + /// Send a pong to a specific connection + /** + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + * @param [out] ec A reference to an error code to fill in + */ + void pong(connection_hdl hdl, std::string const & payload, + lib::error_code & ec); + /// Send a pong to a specific connection + /** + * Exception variant of `pong` + * + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + */ + void pong(connection_hdl hdl, std::string const & payload); + + /// Retrieves a connection_ptr from a connection_hdl (exception free) + /** + * Converting a weak pointer to shared_ptr is not thread safe because the + * pointer could be deleted at any time. + * + * NOTE: This method may be called by handler to upgrade its handle to a + * full connection_ptr. That full connection may then be used safely for the + * remainder of the handler body. get_con_from_hdl and the resulting + * connection_ptr are NOT safe to use outside the handler loop. + * + * @param hdl The connection handle to translate + * + * @return the connection_ptr. May be NULL if the handle was invalid. + */ + connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) { + connection_ptr con = lib::static_pointer_cast( + hdl.lock()); + if (!con) { + ec = error::make_error_code(error::bad_connection); + } + return con; + } + + /// Retrieves a connection_ptr from a connection_hdl (exception version) + connection_ptr get_con_from_hdl(connection_hdl hdl) { + lib::error_code ec; + connection_ptr con = this->get_con_from_hdl(hdl,ec); + if (ec) { + throw exception(ec); + } + return con; + } +protected: + connection_ptr create_connection(); + + lib::shared_ptr m_alog; + lib::shared_ptr m_elog; +private: + // dynamic settings + std::string m_user_agent; + + open_handler m_open_handler; + close_handler m_close_handler; + fail_handler m_fail_handler; + ping_handler m_ping_handler; + pong_handler m_pong_handler; + pong_timeout_handler m_pong_timeout_handler; + interrupt_handler m_interrupt_handler; + http_handler m_http_handler; + validate_handler m_validate_handler; + message_handler m_message_handler; + + long m_open_handshake_timeout_dur; + long m_close_handshake_timeout_dur; + long m_pong_timeout_dur; + size_t m_max_message_size; + size_t m_max_http_body_size; + + rng_type m_rng; + + // static settings + bool const m_is_server; + + // endpoint state + mutable mutex_type m_mutex; +}; + +} // namespace websocketpp + +#include + +#endif // WEBSOCKETPP_ENDPOINT_HPP diff --git a/subprojects/websocketpp/websocketpp/endpoint_base.hpp b/subprojects/websocketpp/websocketpp/endpoint_base.hpp new file mode 100644 index 000000000..1ad1a44dc --- /dev/null +++ b/subprojects/websocketpp/websocketpp/endpoint_base.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ENDPOINT_BASE_HPP +#define WEBSOCKETPP_ENDPOINT_BASE_HPP + +namespace websocketpp { + +/// Stub for user supplied base class. +class endpoint_base {}; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_ENDPOINT_BASE_HPP diff --git a/subprojects/websocketpp/websocketpp/error.hpp b/subprojects/websocketpp/websocketpp/error.hpp new file mode 100644 index 000000000..562fb6ed7 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/error.hpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ERROR_HPP +#define WEBSOCKETPP_ERROR_HPP + +#include +#include +#include + +#include +#include + +namespace websocketpp { + +/// Combination error code / string type for returning two values +typedef std::pair err_str_pair; + +/// Library level error codes +namespace error { +enum value { + /// Catch-all library error + general = 1, + + /// send attempted when endpoint write queue was full + send_queue_full, + + /// Attempted an operation using a payload that was improperly formatted + /// ex: invalid UTF8 encoding on a text message. + payload_violation, + + /// Attempted to open a secure connection with an insecure endpoint + endpoint_not_secure, + + /// Attempted an operation that required an endpoint that is no longer + /// available. This is usually because the endpoint went out of scope + /// before a connection that it created. + endpoint_unavailable, + + /// An invalid uri was supplied + invalid_uri, + + /// The endpoint is out of outgoing message buffers + no_outgoing_buffers, + + /// The endpoint is out of incoming message buffers + no_incoming_buffers, + + /// The connection was in the wrong state for this operation + invalid_state, + + /// Unable to parse close code + bad_close_code, + + /// Close code is in a reserved range + reserved_close_code, + + /// Close code is invalid + invalid_close_code, + + /// Invalid UTF-8 + invalid_utf8, + + /// Invalid subprotocol + invalid_subprotocol, + + /// An operation was attempted on a connection that did not exist or was + /// already deleted. + bad_connection, + + /// Unit testing utility error code + test, + + /// Connection creation attempted failed + con_creation_failed, + + /// Selected subprotocol was not requested by the client + unrequested_subprotocol, + + /// Attempted to use a client specific feature on a server endpoint + client_only, + + /// Attempted to use a server specific feature on a client endpoint + server_only, + + /// HTTP connection ended + http_connection_ended, + + /// WebSocket opening handshake timed out + open_handshake_timeout, + + /// WebSocket close handshake timed out + close_handshake_timeout, + + /// Invalid port in URI + invalid_port, + + /// An async accept operation failed because the underlying transport has been + /// requested to not listen for new connections anymore. + async_accept_not_listening, + + /// The requested operation was canceled + operation_canceled, + + /// Connection rejected + rejected, + + /// Upgrade Required. This happens if an HTTP request is made to a + /// WebSocket++ server that doesn't implement an http handler + upgrade_required, + + /// Invalid WebSocket protocol version + invalid_version, + + /// Unsupported WebSocket protocol version + unsupported_version, + + /// HTTP parse error + http_parse_error, + + /// Extension negotiation failed + extension_neg_failed +}; // enum value + + +class category : public lib::error_category { +public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp"; + } + + std::string message(int value) const { + switch(value) { + case error::general: + return "Generic error"; + case error::send_queue_full: + return "send queue full"; + case error::payload_violation: + return "payload violation"; + case error::endpoint_not_secure: + return "endpoint not secure"; + case error::endpoint_unavailable: + return "endpoint not available"; + case error::invalid_uri: + return "invalid uri"; + case error::no_outgoing_buffers: + return "no outgoing message buffers"; + case error::no_incoming_buffers: + return "no incoming message buffers"; + case error::invalid_state: + return "invalid state"; + case error::bad_close_code: + return "Unable to extract close code"; + case error::invalid_close_code: + return "Extracted close code is in an invalid range"; + case error::reserved_close_code: + return "Extracted close code is in a reserved range"; + case error::invalid_utf8: + return "Invalid UTF-8"; + case error::invalid_subprotocol: + return "Invalid subprotocol"; + case error::bad_connection: + return "Bad Connection"; + case error::test: + return "Test Error"; + case error::con_creation_failed: + return "Connection creation attempt failed"; + case error::unrequested_subprotocol: + return "Selected subprotocol was not requested by the client"; + case error::client_only: + return "Feature not available on server endpoints"; + case error::server_only: + return "Feature not available on client endpoints"; + case error::http_connection_ended: + return "HTTP connection ended"; + case error::open_handshake_timeout: + return "The opening handshake timed out"; + case error::close_handshake_timeout: + return "The closing handshake timed out"; + case error::invalid_port: + return "Invalid URI port"; + case error::async_accept_not_listening: + return "Async Accept not listening"; + case error::operation_canceled: + return "Operation canceled"; + case error::rejected: + return "Connection rejected"; + case error::upgrade_required: + return "Upgrade required"; + case error::invalid_version: + return "Invalid version"; + case error::unsupported_version: + return "Unsupported version"; + case error::http_parse_error: + return "HTTP parse error"; + case error::extension_neg_failed: + return "Extension negotiation failed"; + default: + return "Unknown"; + } + } +}; + +inline const lib::error_category& get_category() { + static category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +namespace websocketpp { + +class exception : public std::exception { +public: + exception(std::string const & msg, lib::error_code ec = make_error_code(error::general)) + : m_msg(msg.empty() ? ec.message() : msg), m_code(ec) + {} + + explicit exception(lib::error_code ec) + : m_msg(ec.message()), m_code(ec) + {} + + ~exception() throw() {} + + virtual char const * what() const throw() { + return m_msg.c_str(); + } + + lib::error_code code() const throw() { + return m_code; + } + + const std::string m_msg; + lib::error_code m_code; +}; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_ERROR_HPP diff --git a/subprojects/websocketpp/websocketpp/extensions/extension.hpp b/subprojects/websocketpp/websocketpp/extensions/extension.hpp new file mode 100644 index 000000000..f5fbd9f7b --- /dev/null +++ b/subprojects/websocketpp/websocketpp/extensions/extension.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_EXTENSION_HPP +#define WEBSOCKETPP_EXTENSION_HPP + +#include +#include + +#include +#include + +namespace websocketpp { + +/** + * Some generic information about extensions + * + * Each extension object has an implemented flag. It can be retrieved by calling + * is_implemented(). This compile time flag indicates whether or not the object + * in question actually implements the extension or if it is a placeholder stub + * + * Each extension object also has an enabled flag. It can be retrieved by + * calling is_enabled(). This runtime flag indicates whether or not the + * extension has been negotiated for this connection. + */ +namespace extensions { + +namespace error { +enum value { + /// Catch all + general = 1, + + /// Extension disabled + disabled +}; + +class category : public lib::error_category { +public: + category() {} + + const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.extension"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic extension error"; + case disabled: + return "Use of methods from disabled extension"; + default: + return "Unknown permessage-compress error"; + } + } +}; + +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace extensions +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum + +{ + static const bool value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_EXTENSION_HPP diff --git a/subprojects/websocketpp/websocketpp/extensions/permessage_deflate/disabled.hpp b/subprojects/websocketpp/websocketpp/extensions/permessage_deflate/disabled.hpp new file mode 100644 index 000000000..657309520 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/extensions/permessage_deflate/disabled.hpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP +#define WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP + +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace websocketpp { +namespace extensions { +namespace permessage_deflate { + +/// Stub class for use when disabling permessage_deflate extension +/** + * This class is a stub that implements the permessage_deflate interface + * with minimal dependencies. It is used to disable permessage_deflate + * functionality at compile time without loading any unnecessary code. + */ +template +class disabled { + typedef std::pair err_str_pair; + +public: + /// Negotiate extension + /** + * The disabled extension always fails the negotiation with a disabled + * error. + * + * @param offer Attribute from client's offer + * @return Status code and value to return to remote endpoint + */ + err_str_pair negotiate(http::attribute_list const &) { + return make_pair(make_error_code(error::disabled),std::string()); + } + + /// Initialize state + /** + * For the disabled extension state initialization is a no-op. + * + * @param is_server True to initialize as a server, false for a client. + * @return A code representing the error that occurred, if any + */ + lib::error_code init(bool) { + return lib::error_code(); + } + + /// Returns true if the extension is capable of providing + /// permessage_deflate functionality + bool is_implemented() const { + return false; + } + + /// Returns true if permessage_deflate functionality is active for this + /// connection + bool is_enabled() const { + return false; + } + + /// Generate extension offer + /** + * Creates an offer string to include in the Sec-WebSocket-Extensions + * header of outgoing client requests. + * + * @return A WebSocket extension offer string for this extension + */ + std::string generate_offer() const { + return ""; + } + + /// Compress bytes + /** + * @param [in] in String to compress + * @param [out] out String to append compressed bytes to + * @return Error or status code + */ + lib::error_code compress(std::string const &, std::string &) { + return make_error_code(error::disabled); + } + + /// Decompress bytes + /** + * @param buf Byte buffer to decompress + * @param len Length of buf + * @param out String to append decompressed bytes to + * @return Error or status code + */ + lib::error_code decompress(uint8_t const *, size_t, std::string &) { + return make_error_code(error::disabled); + } +}; + +} // namespace permessage_deflate +} // namespace extensions +} // namespace websocketpp + +#endif // WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP diff --git a/subprojects/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp b/subprojects/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp new file mode 100644 index 000000000..d05403a8a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp @@ -0,0 +1,817 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP +#define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP + + +#include +#include +#include +#include +#include +#include + +#include + +#include "zlib.h" + +#include +#include +#include + +namespace websocketpp { +namespace extensions { + +/// Implementation of RFC 7692, the permessage-deflate WebSocket extension +/** + * ### permessage-deflate interface + * + * **init**\n + * `lib::error_code init(bool is_server)`\n + * Performs initialization + * + * **is_implimented**\n + * `bool is_implimented()`\n + * Returns whether or not the object impliments the extension or not + * + * **is_enabled**\n + * `bool is_enabled()`\n + * Returns whether or not the extension was negotiated for the current + * connection + * + * **generate_offer**\n + * `std::string generate_offer() const`\n + * Create an extension offer string based on local policy + * + * **validate_response**\n + * `lib::error_code validate_response(http::attribute_list const & response)`\n + * Negotiate the parameters of extension use + * + * **negotiate**\n + * `err_str_pair negotiate(http::attribute_list const & attributes)`\n + * Negotiate the parameters of extension use + * + * **compress**\n + * `lib::error_code compress(std::string const & in, std::string & out)`\n + * Compress the bytes in `in` and append them to `out` + * + * **decompress**\n + * `lib::error_code decompress(uint8_t const * buf, size_t len, std::string & + * out)`\n + * Decompress `len` bytes from `buf` and append them to string `out` + */ +namespace permessage_deflate { + +/// Permessage deflate error values +namespace error { +enum value { + /// Catch all + general = 1, + + /// Invalid extension attributes + invalid_attributes, + + /// Invalid extension attribute value + invalid_attribute_value, + + /// Invalid megotiation mode + invalid_mode, + + /// Unsupported extension attributes + unsupported_attributes, + + /// Invalid value for max_window_bits + invalid_max_window_bits, + + /// ZLib Error + zlib_error, + + /// Uninitialized + uninitialized, +}; + +/// Permessage-deflate error category +class category : public lib::error_category { +public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.extension.permessage-deflate"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic permessage-compress error"; + case invalid_attributes: + return "Invalid extension attributes"; + case invalid_attribute_value: + return "Invalid extension attribute value"; + case invalid_mode: + return "Invalid permessage-deflate negotiation mode"; + case unsupported_attributes: + return "Unsupported extension attributes"; + case invalid_max_window_bits: + return "Invalid value for max_window_bits"; + case zlib_error: + return "A zlib function returned an error"; + case uninitialized: + return "Deflate extension must be initialized before use"; + default: + return "Unknown permessage-compress error"; + } + } +}; + +/// Get a reference to a static copy of the permessage-deflate error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Create an error code in the permessage-deflate category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace permessage_deflate +} // namespace extensions +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum + +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ +namespace websocketpp { +namespace extensions { +namespace permessage_deflate { + +/// Default value for server_max_window_bits as defined by RFC 7692 +static uint8_t const default_server_max_window_bits = 15; +/// Minimum value for server_max_window_bits as defined by RFC 7692 +/** + * NOTE: A value of 8 is not actually supported by zlib, the deflate + * library that WebSocket++ uses. To preserve backwards compatibility + * with RFC 7692 and previous versions of the library a value of 8 + * is accepted by the library but will always be negotiated as 9. + */ +static uint8_t const min_server_max_window_bits = 8; +/// Maximum value for server_max_window_bits as defined by RFC 7692 +static uint8_t const max_server_max_window_bits = 15; + +/// Default value for client_max_window_bits as defined by RFC 7692 +static uint8_t const default_client_max_window_bits = 15; +/// Minimum value for client_max_window_bits as defined by RFC 7692 +/** + * NOTE: A value of 8 is not actually supported by zlib, the deflate + * library that WebSocket++ uses. To preserve backwards compatibility + * with RFC 7692 and previous versions of the library a value of 8 + * is accepted by the library but will always be negotiated as 9. + */ +static uint8_t const min_client_max_window_bits = 8; +/// Maximum value for client_max_window_bits as defined by RFC 7692 +static uint8_t const max_client_max_window_bits = 15; + +namespace mode { +enum value { + /// Accept any value the remote endpoint offers + accept = 1, + /// Decline any value the remote endpoint offers. Insist on defaults. + decline, + /// Use the largest value common to both offers + largest, + /// Use the smallest value common to both offers + smallest +}; +} // namespace mode + +template +class enabled { +public: + enabled() + : m_enabled(false) + , m_server_no_context_takeover(false) + , m_client_no_context_takeover(false) + , m_server_max_window_bits(15) + , m_client_max_window_bits(15) + , m_server_max_window_bits_mode(mode::accept) + , m_client_max_window_bits_mode(mode::accept) + , m_initialized(false) + , m_compress_buffer_size(8192) + { + m_dstate.zalloc = Z_NULL; + m_dstate.zfree = Z_NULL; + m_dstate.opaque = Z_NULL; + + m_istate.zalloc = Z_NULL; + m_istate.zfree = Z_NULL; + m_istate.opaque = Z_NULL; + m_istate.avail_in = 0; + m_istate.next_in = Z_NULL; + } + + ~enabled() { + if (!m_initialized) { + return; + } + + int ret = deflateEnd(&m_dstate); + + if (ret != Z_OK) { + //std::cout << "error cleaning up zlib compression state" + // << std::endl; + } + + ret = inflateEnd(&m_istate); + + if (ret != Z_OK) { + //std::cout << "error cleaning up zlib decompression state" + // << std::endl; + } + } + + /// Initialize zlib state + /** + * Note: this should be called *after* the negotiation methods. It will use + * information from the negotiation to determine how to initialize the zlib + * data structures. + * + * @todo memory level, strategy, etc are hardcoded + * + * @param is_server True to initialize as a server, false for a client. + * @return A code representing the error that occurred, if any + */ + lib::error_code init(bool is_server) { + uint8_t deflate_bits; + uint8_t inflate_bits; + + if (is_server) { + deflate_bits = m_server_max_window_bits; + inflate_bits = m_client_max_window_bits; + } else { + deflate_bits = m_client_max_window_bits; + inflate_bits = m_server_max_window_bits; + } + + int ret = deflateInit2( + &m_dstate, + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + -1*deflate_bits, + 4, // memory level 1-9 + Z_DEFAULT_STRATEGY + ); + + if (ret != Z_OK) { + return make_error_code(error::zlib_error); + } + + ret = inflateInit2( + &m_istate, + -1*inflate_bits + ); + + if (ret != Z_OK) { + return make_error_code(error::zlib_error); + } + + m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]); + m_decompress_buffer.reset(new unsigned char[m_compress_buffer_size]); + if ((m_server_no_context_takeover && is_server) || + (m_client_no_context_takeover && !is_server)) + { + m_flush = Z_FULL_FLUSH; + } else { + m_flush = Z_SYNC_FLUSH; + } + m_initialized = true; + return lib::error_code(); + } + + /// Test if this object implements the permessage-deflate specification + /** + * Because this object does implieent it, it will always return true. + * + * @return Whether or not this object implements permessage-deflate + */ + bool is_implemented() const { + return true; + } + + /// Test if the extension was negotiated for this connection + /** + * Retrieves whether or not this extension is in use based on the initial + * handshake extension negotiations. + * + * @return Whether or not the extension is in use + */ + bool is_enabled() const { + return m_enabled; + } + + /// Reset server's outgoing LZ77 sliding window for each new message + /** + * Enabling this setting will cause the server's compressor to reset the + * compression state (the LZ77 sliding window) for every message. This + * means that the compressor will not look back to patterns in previous + * messages to improve compression. This will reduce the compression + * efficiency for large messages somewhat and small messages drastically. + * + * This option may reduce server compressor memory usage and client + * decompressor memory usage. + * @todo Document to what extent memory usage will be reduced + * + * For clients, this option is dependent on server support. Enabling it + * via this method does not guarantee that it will be successfully + * negotiated, only that it will be requested. + * + * For servers, no client support is required. Enabling this option on a + * server will result in its use. The server will signal to clients that + * the option will be in use so they can optimize resource usage if they + * are able. + */ + void enable_server_no_context_takeover() { + m_server_no_context_takeover = true; + } + + /// Reset client's outgoing LZ77 sliding window for each new message + /** + * Enabling this setting will cause the client's compressor to reset the + * compression state (the LZ77 sliding window) for every message. This + * means that the compressor will not look back to patterns in previous + * messages to improve compression. This will reduce the compression + * efficiency for large messages somewhat and small messages drastically. + * + * This option may reduce client compressor memory usage and server + * decompressor memory usage. + * @todo Document to what extent memory usage will be reduced + * + * This option is supported by all compliant clients and servers. Enabling + * it via either endpoint should be sufficient to ensure it is used. + */ + void enable_client_no_context_takeover() { + m_client_no_context_takeover = true; + } + + /// Limit server LZ77 sliding window size + /** + * The bits setting is the base 2 logarithm of the maximum window size that + * the server must use to compress outgoing messages. The permitted range + * is 9 to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB + * window. The default setting is 15. + * + * Mode Options: + * - accept: Accept whatever the remote endpoint offers. + * - decline: Decline any offers to deviate from the defaults + * - largest: Accept largest window size acceptable to both endpoints + * - smallest: Accept smallest window size acceptiable to both endpoints + * + * This setting is dependent on server support. A client requesting this + * setting may be rejected by the server or have the exact value used + * adjusted by the server. A server may unilaterally set this value without + * client support. + * + * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed. + * Prior to version 0.8.0 a value of 8 was also allowed by this library. + * zlib, the deflate compression library that WebSocket++ uses has always + * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9 + * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0 + * continues to perform the 8->9 conversion for backwards compatibility + * purposes but this should be considered deprecated functionality. + * + * @param bits The size to request for the outgoing window size + * @param mode The mode to use for negotiating this parameter + * @return A status code + */ + lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode) { + if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) { + return error::make_error_code(error::invalid_max_window_bits); + } + + // See note in doc comment above about what is happening here + if (bits == 8) { + bits = 9; + } + + m_server_max_window_bits = bits; + m_server_max_window_bits_mode = mode; + + return lib::error_code(); + } + + /// Limit client LZ77 sliding window size + /** + * The bits setting is the base 2 logarithm of the window size that the + * client must use to compress outgoing messages. The permitted range is 9 + * to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB window. + * The default setting is 15. + * + * Mode Options: + * - accept: Accept whatever the remote endpoint offers. + * - decline: Decline any offers to deviate from the defaults + * - largest: Accept largest window size acceptable to both endpoints + * - smallest: Accept smallest window size acceptiable to both endpoints + * + * This setting is dependent on client support. A client may limit its own + * outgoing window size unilaterally. A server may only limit the client's + * window size if the remote client supports that feature. + * + * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed. + * Prior to version 0.8.0 a value of 8 was also allowed by this library. + * zlib, the deflate compression library that WebSocket++ uses has always + * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9 + * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0 + * continues to perform the 8->9 conversion for backwards compatibility + * purposes but this should be considered deprecated functionality. + * + * @param bits The size to request for the outgoing window size + * @param mode The mode to use for negotiating this parameter + * @return A status code + */ + lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode) { + if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) { + return error::make_error_code(error::invalid_max_window_bits); + } + + // See note in doc comment above about what is happening here + if (bits == 8) { + bits = 9; + } + + m_client_max_window_bits = bits; + m_client_max_window_bits_mode = mode; + + return lib::error_code(); + } + + /// Generate extension offer + /** + * Creates an offer string to include in the Sec-WebSocket-Extensions + * header of outgoing client requests. + * + * @return A WebSocket extension offer string for this extension + */ + std::string generate_offer() const { + // TODO: this should be dynamically generated based on user settings + return "permessage-deflate; client_no_context_takeover; client_max_window_bits"; + } + + /// Validate extension response + /** + * Confirm that the server has negotiated settings compatible with our + * original offer and apply those settings to the extension state. + * + * @param response The server response attribute list to validate + * @return Validation error or 0 on success + */ + lib::error_code validate_offer(http::attribute_list const &) { + return lib::error_code(); + } + + /// Negotiate extension + /** + * Confirm that the client's extension negotiation offer has settings + * compatible with local policy. If so, generate a reply and apply those + * settings to the extension state. + * + * @param offer Attribute from client's offer + * @return Status code and value to return to remote endpoint + */ + err_str_pair negotiate(http::attribute_list const & offer) { + err_str_pair ret; + + http::attribute_list::const_iterator it; + for (it = offer.begin(); it != offer.end(); ++it) { + if (it->first == "server_no_context_takeover") { + negotiate_server_no_context_takeover(it->second,ret.first); + } else if (it->first == "client_no_context_takeover") { + negotiate_client_no_context_takeover(it->second,ret.first); + } else if (it->first == "server_max_window_bits") { + negotiate_server_max_window_bits(it->second,ret.first); + } else if (it->first == "client_max_window_bits") { + negotiate_client_max_window_bits(it->second,ret.first); + } else { + ret.first = make_error_code(error::invalid_attributes); + } + + if (ret.first) { + break; + } + } + + if (ret.first == lib::error_code()) { + m_enabled = true; + ret.second = generate_response(); + } + + return ret; + } + + /// Compress bytes + /** + * @todo: avail_in/out is 32 bit, need to fix for cases of >32 bit frames + * on 64 bit machines. + * + * @param [in] in String to compress + * @param [out] out String to append compressed bytes to + * @return Error or status code + */ + lib::error_code compress(std::string const & in, std::string & out) { + if (!m_initialized) { + return make_error_code(error::uninitialized); + } + + size_t output; + + if (in.empty()) { + uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff}; + out.append((char *)(buf),6); + return lib::error_code(); + } + + m_dstate.avail_in = in.size(); + m_dstate.next_in = (unsigned char *)(const_cast(in.data())); + + do { + // Output to local buffer + m_dstate.avail_out = m_compress_buffer_size; + m_dstate.next_out = m_compress_buffer.get(); + + deflate(&m_dstate, m_flush); + + output = m_compress_buffer_size - m_dstate.avail_out; + + out.append((char *)(m_compress_buffer.get()),output); + } while (m_dstate.avail_out == 0); + + return lib::error_code(); + } + + /// Decompress bytes + /** + * @param buf Byte buffer to decompress + * @param len Length of buf + * @param out String to append decompressed bytes to + * @return Error or status code + */ + lib::error_code decompress(uint8_t const * buf, size_t len, std::string & + out) + { + if (!m_initialized) { + return make_error_code(error::uninitialized); + } + + int ret; + + m_istate.avail_in = len; + m_istate.next_in = const_cast(buf); + + do { + m_istate.avail_out = m_compress_buffer_size; + m_istate.next_out = m_decompress_buffer.get(); + + ret = inflate(&m_istate, Z_SYNC_FLUSH); + + if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { + return make_error_code(error::zlib_error); + } + + out.append( + reinterpret_cast(m_decompress_buffer.get()), + m_compress_buffer_size - m_istate.avail_out + ); + } while (m_istate.avail_out == 0); + + return lib::error_code(); + } +private: + /// Generate negotiation response + /** + * @return Generate extension negotiation reponse string to send to client + */ + std::string generate_response() { + std::string ret = "permessage-deflate"; + + if (m_server_no_context_takeover) { + ret += "; server_no_context_takeover"; + } + + if (m_client_no_context_takeover) { + ret += "; client_no_context_takeover"; + } + + if (m_server_max_window_bits < default_server_max_window_bits) { + std::stringstream s; + s << int(m_server_max_window_bits); + ret += "; server_max_window_bits="+s.str(); + } + + if (m_client_max_window_bits < default_client_max_window_bits) { + std::stringstream s; + s << int(m_client_max_window_bits); + ret += "; client_max_window_bits="+s.str(); + } + + return ret; + } + + /// Negotiate server_no_context_takeover attribute + /** + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_server_no_context_takeover(std::string const & value, + lib::error_code & ec) + { + if (!value.empty()) { + ec = make_error_code(error::invalid_attribute_value); + return; + } + + m_server_no_context_takeover = true; + } + + /// Negotiate client_no_context_takeover attribute + /** + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_client_no_context_takeover(std::string const & value, + lib::error_code & ec) + { + if (!value.empty()) { + ec = make_error_code(error::invalid_attribute_value); + return; + } + + m_client_no_context_takeover = true; + } + + /// Negotiate server_max_window_bits attribute + /** + * When this method starts, m_server_max_window_bits will contain the server's + * preferred value and m_server_max_window_bits_mode will contain the mode the + * server wants to use to for negotiation. `value` contains the value the + * client requested that we use. + * + * options: + * - decline (ignore value, offer our default instead) + * - accept (use the value requested by the client) + * - largest (use largest value acceptable to both) + * - smallest (use smallest possible value) + * + * NOTE: As a value of 8 is no longer explicitly supported by zlib but might + * be requested for negotiation by an older client/server, if the result of + * the negotiation would be to send a value of 8, a value of 9 is offered + * instead. This ensures that WebSocket++ will only ever negotiate connections + * with compression settings explicitly supported by zlib. + * + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_server_max_window_bits(std::string const & value, + lib::error_code & ec) + { + uint8_t bits = uint8_t(atoi(value.c_str())); + + if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) { + ec = make_error_code(error::invalid_attribute_value); + m_server_max_window_bits = default_server_max_window_bits; + return; + } + + switch (m_server_max_window_bits_mode) { + case mode::decline: + m_server_max_window_bits = default_server_max_window_bits; + break; + case mode::accept: + m_server_max_window_bits = bits; + break; + case mode::largest: + m_server_max_window_bits = std::min(bits,m_server_max_window_bits); + break; + case mode::smallest: + m_server_max_window_bits = min_server_max_window_bits; + break; + default: + ec = make_error_code(error::invalid_mode); + m_server_max_window_bits = default_server_max_window_bits; + } + + // See note in doc comment + if (m_server_max_window_bits == 8) { + m_server_max_window_bits = 9; + } + } + + /// Negotiate client_max_window_bits attribute + /** + * When this method starts, m_client_max_window_bits and m_c2s_max_window_mode + * will contain the server's preferred values for window size and + * negotiation mode. + * + * options: + * - decline (ignore value, offer our default instead) + * - accept (use the value requested by the client) + * - largest (use largest value acceptable to both) + * - smallest (use smallest possible value) + * + * NOTE: As a value of 8 is no longer explicitly supported by zlib but might + * be requested for negotiation by an older client/server, if the result of + * the negotiation would be to send a value of 8, a value of 9 is offered + * instead. This ensures that WebSocket++ will only ever negotiate connections + * with compression settings explicitly supported by zlib. + * + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_client_max_window_bits(std::string const & value, + lib::error_code & ec) + { + uint8_t bits = uint8_t(atoi(value.c_str())); + + if (value.empty()) { + bits = default_client_max_window_bits; + } else if (bits < min_client_max_window_bits || + bits > max_client_max_window_bits) + { + ec = make_error_code(error::invalid_attribute_value); + m_client_max_window_bits = default_client_max_window_bits; + return; + } + + switch (m_client_max_window_bits_mode) { + case mode::decline: + m_client_max_window_bits = default_client_max_window_bits; + break; + case mode::accept: + m_client_max_window_bits = bits; + break; + case mode::largest: + m_client_max_window_bits = std::min(bits,m_client_max_window_bits); + break; + case mode::smallest: + m_client_max_window_bits = min_client_max_window_bits; + break; + default: + ec = make_error_code(error::invalid_mode); + m_client_max_window_bits = default_client_max_window_bits; + } + + // See note in doc comment + if (m_client_max_window_bits == 8) { + m_client_max_window_bits = 9; + } + } + + bool m_enabled; + bool m_server_no_context_takeover; + bool m_client_no_context_takeover; + uint8_t m_server_max_window_bits; + uint8_t m_client_max_window_bits; + mode::value m_server_max_window_bits_mode; + mode::value m_client_max_window_bits_mode; + + bool m_initialized; + int m_flush; + size_t m_compress_buffer_size; + lib::unique_ptr_uchar_array m_compress_buffer; + lib::unique_ptr_uchar_array m_decompress_buffer; + z_stream m_dstate; + z_stream m_istate; +}; + +} // namespace permessage_deflate +} // namespace extensions +} // namespace websocketpp + +#endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP diff --git a/subprojects/websocketpp/websocketpp/frame.hpp b/subprojects/websocketpp/websocketpp/frame.hpp new file mode 100644 index 000000000..18a990b36 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/frame.hpp @@ -0,0 +1,864 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_FRAME_HPP +#define WEBSOCKETPP_FRAME_HPP + +#include +#include + +#include +#include + +#include + +namespace websocketpp { +/// Data structures and utility functions for manipulating WebSocket frames +/** + * namespace frame provides a number of data structures and utility functions + * for reading, writing, and manipulating binary encoded WebSocket frames. + */ +namespace frame { + +/// Minimum length of a WebSocket frame header. +static unsigned int const BASIC_HEADER_LENGTH = 2; +/// Maximum length of a WebSocket header +static unsigned int const MAX_HEADER_LENGTH = 14; +/// Maximum length of the variable portion of the WebSocket header +static unsigned int const MAX_EXTENDED_HEADER_LENGTH = 12; + +/// Two byte conversion union +union uint16_converter { + uint16_t i; + uint8_t c[2]; +}; + +/// Four byte conversion union +union uint32_converter { + uint32_t i; + uint8_t c[4]; +}; + +/// Eight byte conversion union +union uint64_converter { + uint64_t i; + uint8_t c[8]; +}; + +/// Constants and utility functions related to WebSocket opcodes +/** + * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2. + */ +namespace opcode { + enum value { + continuation = 0x0, + text = 0x1, + binary = 0x2, + rsv3 = 0x3, + rsv4 = 0x4, + rsv5 = 0x5, + rsv6 = 0x6, + rsv7 = 0x7, + close = 0x8, + ping = 0x9, + pong = 0xA, + control_rsvb = 0xB, + control_rsvc = 0xC, + control_rsvd = 0xD, + control_rsve = 0xE, + control_rsvf = 0xF, + + CONTINUATION = 0x0, + TEXT = 0x1, + BINARY = 0x2, + RSV3 = 0x3, + RSV4 = 0x4, + RSV5 = 0x5, + RSV6 = 0x6, + RSV7 = 0x7, + CLOSE = 0x8, + PING = 0x9, + PONG = 0xA, + CONTROL_RSVB = 0xB, + CONTROL_RSVC = 0xC, + CONTROL_RSVD = 0xD, + CONTROL_RSVE = 0xE, + CONTROL_RSVF = 0xF + }; + + /// Check if an opcode is reserved + /** + * @param v The opcode to test. + * @return Whether or not the opcode is reserved. + */ + inline bool reserved(value v) { + return (v >= rsv3 && v <= rsv7) || + (v >= control_rsvb && v <= control_rsvf); + } + + /// Check if an opcode is invalid + /** + * Invalid opcodes are negative or require greater than 4 bits to store. + * + * @param v The opcode to test. + * @return Whether or not the opcode is invalid. + */ + inline bool invalid(value v) { + return (v > 0xF || v < 0); + } + + /// Check if an opcode is for a control frame + /** + * @param v The opcode to test. + * @return Whether or not the opcode is a control opcode. + */ + inline bool is_control(value v) { + return v >= 0x8; + } +} + +/// Constants related to frame and payload limits +namespace limits { + /// Minimum length of a WebSocket frame header. + static unsigned int const basic_header_length = 2; + + /// Maximum length of a WebSocket header + static unsigned int const max_header_length = 14; + + /// Maximum length of the variable portion of the WebSocket header + static unsigned int const max_extended_header_length = 12; + + /// Maximum size of a basic WebSocket payload + static uint8_t const payload_size_basic = 125; + + /// Maximum size of an extended WebSocket payload (basic payload = 126) + static uint16_t const payload_size_extended = 0xFFFF; // 2^16, 65535 + + /// Maximum size of a jumbo WebSocket payload (basic payload = 127) + static uint64_t const payload_size_jumbo = 0x7FFFFFFFFFFFFFFFLL;//2^63 + + /// Maximum size of close frame reason + /** + * This is payload_size_basic - 2 bytes (as first two bytes are used for + * the close code + */ + static uint8_t const close_reason_size = 123; +} + + +// masks for fields in the basic header +static uint8_t const BHB0_OPCODE = 0x0F; +static uint8_t const BHB0_RSV3 = 0x10; +static uint8_t const BHB0_RSV2 = 0x20; +static uint8_t const BHB0_RSV1 = 0x40; +static uint8_t const BHB0_FIN = 0x80; + +static uint8_t const BHB1_PAYLOAD = 0x7F; +static uint8_t const BHB1_MASK = 0x80; + +static uint8_t const payload_size_code_16bit = 0x7E; // 126 +static uint8_t const payload_size_code_64bit = 0x7F; // 127 + +typedef uint32_converter masking_key_type; + +/// The constant size component of a WebSocket frame header +struct basic_header { + basic_header() : b0(0x00),b1(0x00) {} + + basic_header(uint8_t p0, uint8_t p1) : b0(p0), b1(p1) {} + + basic_header(opcode::value op, uint64_t size, bool fin, bool mask, + bool rsv1 = false, bool rsv2 = false, bool rsv3 = false) : b0(0x00), + b1(0x00) + { + if (fin) { + b0 |= BHB0_FIN; + } + if (rsv1) { + b0 |= BHB0_RSV1; + } + if (rsv2) { + b0 |= BHB0_RSV2; + } + if (rsv3) { + b0 |= BHB0_RSV3; + } + b0 |= (op & BHB0_OPCODE); + + if (mask) { + b1 |= BHB1_MASK; + } + + uint8_t basic_value; + + if (size <= limits::payload_size_basic) { + basic_value = static_cast(size); + } else if (size <= limits::payload_size_extended) { + basic_value = payload_size_code_16bit; + } else { + basic_value = payload_size_code_64bit; + } + + + b1 |= basic_value; + } + + uint8_t b0; + uint8_t b1; +}; + +/// The variable size component of a WebSocket frame header +struct extended_header { + extended_header() { + std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00); + } + + extended_header(uint64_t payload_size) { + std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00); + + copy_payload(payload_size); + } + + extended_header(uint64_t payload_size, uint32_t masking_key) { + std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00); + + // Copy payload size + int offset = copy_payload(payload_size); + + // Copy Masking Key + uint32_converter temp32; + temp32.i = masking_key; + std::copy(temp32.c,temp32.c+4,bytes+offset); + } + + uint8_t bytes[MAX_EXTENDED_HEADER_LENGTH]; +private: + int copy_payload(uint64_t payload_size) { + int payload_offset = 0; + + if (payload_size <= limits::payload_size_basic) { + payload_offset = 8; + } else if (payload_size <= limits::payload_size_extended) { + payload_offset = 6; + } + + uint64_converter temp64; + temp64.i = lib::net::_htonll(payload_size); + std::copy(temp64.c+payload_offset,temp64.c+8,bytes); + + return 8-payload_offset; + } +}; + +bool get_fin(basic_header const &h); +void set_fin(basic_header &h, bool value); +bool get_rsv1(basic_header const &h); +void set_rsv1(basic_header &h, bool value); +bool get_rsv2(basic_header const &h); +void set_rsv2(basic_header &h, bool value); +bool get_rsv3(basic_header const &h); +void set_rsv3(basic_header &h, bool value); +opcode::value get_opcode(basic_header const &h); +bool get_masked(basic_header const &h); +void set_masked(basic_header &h, bool value); +uint8_t get_basic_size(basic_header const &); +size_t get_header_len(basic_header const &); +unsigned int get_masking_key_offset(basic_header const &); + +std::string write_header(basic_header const &, extended_header const &); +masking_key_type get_masking_key(basic_header const &, extended_header const &); +uint16_t get_extended_size(extended_header const &); +uint64_t get_jumbo_size(extended_header const &); +uint64_t get_payload_size(basic_header const &, extended_header const &); + +size_t prepare_masking_key(masking_key_type const & key); +size_t circshift_prepared_key(size_t prepared_key, size_t offset); + +// Functions for performing xor based masking and unmasking +template +void byte_mask(input_iter b, input_iter e, output_iter o, masking_key_type + const & key, size_t key_offset = 0); +template +void byte_mask(iter_type b, iter_type e, masking_key_type const & key, + size_t key_offset = 0); +void word_mask_exact(uint8_t * input, uint8_t * output, size_t length, + masking_key_type const & key); +void word_mask_exact(uint8_t * data, size_t length, masking_key_type const & + key); +size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length, + size_t prepared_key); +size_t word_mask_circ(uint8_t * data, size_t length, size_t prepared_key); + +/// Check whether the frame's FIN bit is set. +/** + * @param [in] h The basic header to extract from. + * @return True if the header's fin bit is set. + */ +inline bool get_fin(basic_header const & h) { + return ((h.b0 & BHB0_FIN) == BHB0_FIN); +} + +/// Set the frame's FIN bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_fin(basic_header & h, bool value) { + h.b0 = (value ? h.b0 | BHB0_FIN : h.b0 & ~BHB0_FIN); +} + +/// check whether the frame's RSV1 bit is set +/** + * @param [in] h The basic header to extract from. + * @return True if the header's RSV1 bit is set. + */ +inline bool get_rsv1(const basic_header &h) { + return ((h.b0 & BHB0_RSV1) == BHB0_RSV1); +} + +/// Set the frame's RSV1 bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_rsv1(basic_header &h, bool value) { + h.b0 = (value ? h.b0 | BHB0_RSV1 : h.b0 & ~BHB0_RSV1); +} + +/// check whether the frame's RSV2 bit is set +/** + * @param [in] h The basic header to extract from. + * @return True if the header's RSV2 bit is set. + */ +inline bool get_rsv2(const basic_header &h) { + return ((h.b0 & BHB0_RSV2) == BHB0_RSV2); +} + +/// Set the frame's RSV2 bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_rsv2(basic_header &h, bool value) { + h.b0 = (value ? h.b0 | BHB0_RSV2 : h.b0 & ~BHB0_RSV2); +} + +/// check whether the frame's RSV3 bit is set +/** + * @param [in] h The basic header to extract from. + * @return True if the header's RSV3 bit is set. + */ +inline bool get_rsv3(const basic_header &h) { + return ((h.b0 & BHB0_RSV3) == BHB0_RSV3); +} + +/// Set the frame's RSV3 bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_rsv3(basic_header &h, bool value) { + h.b0 = (value ? h.b0 | BHB0_RSV3 : h.b0 & ~BHB0_RSV3); +} + +/// Extract opcode from basic header +/** + * @param [in] h The basic header to extract from. + * @return The opcode value of the header. + */ +inline opcode::value get_opcode(const basic_header &h) { + return opcode::value(h.b0 & BHB0_OPCODE); +} + +/// check whether the frame is masked +/** + * @param [in] h The basic header to extract from. + * @return True if the header mask bit is set. + */ +inline bool get_masked(basic_header const & h) { + return ((h.b1 & BHB1_MASK) == BHB1_MASK); +} + +/// Set the frame's MASK bit +/** + * @param [out] h Header to set. + * @param value Value to set it to. + */ +inline void set_masked(basic_header & h, bool value) { + h.b1 = (value ? h.b1 | BHB1_MASK : h.b1 & ~BHB1_MASK); +} + +/// Extracts the raw payload length specified in the basic header +/** + * A basic WebSocket frame header contains a 7 bit value that represents the + * payload size. There are two reserved values that are used to indicate that + * the actual payload size will not fit in 7 bits and that the full payload + * size is included in a separate field. The values are as follows: + * + * PAYLOAD_SIZE_CODE_16BIT (0x7E) indicates that the actual payload is less + * than 16 bit + * + * PAYLOAD_SIZE_CODE_64BIT (0x7F) indicates that the actual payload is less + * than 63 bit + * + * @param [in] h Basic header to read value from. + * @return The exact size encoded in h. + */ +inline uint8_t get_basic_size(const basic_header &h) { + return h.b1 & BHB1_PAYLOAD; +} + +/// Calculates the full length of the header based on the first bytes. +/** + * A WebSocket frame header always has at least two bytes. Encoded within the + * first two bytes is all the information necessary to calculate the full + * (variable) header length. get_header_len() calculates the full header + * length for the given two byte basic header. + * + * @param h Basic frame header to extract size from. + * @return Full length of the extended header. + */ +inline size_t get_header_len(basic_header const & h) { + // TODO: check extensions? + + // masking key offset represents the space used for the extended length + // fields + size_t size = BASIC_HEADER_LENGTH + get_masking_key_offset(h); + + // If the header is masked there is a 4 byte masking key + if (get_masked(h)) { + size += 4; + } + + return size; +} + +/// Calculate the offset location of the masking key within the extended header +/** + * Calculate the offset location of the masking key within the extended header + * using information from its corresponding basic header + * + * @param h Corresponding basic header to calculate from. + * + * @return byte offset of the first byte of the masking key + */ +inline unsigned int get_masking_key_offset(const basic_header &h) { + if (get_basic_size(h) == payload_size_code_16bit) { + return 2; + } else if (get_basic_size(h) == payload_size_code_64bit) { + return 8; + } else { + return 0; + } +} + +/// Generate a properly sized contiguous string that encodes a full frame header +/** + * Copy the basic header h and extended header e into a properly sized + * contiguous frame header string for the purposes of writing out to the wire. + * + * @param h The basic header to include + * @param e The extended header to include + * + * @return A contiguous string containing h and e + */ +inline std::string prepare_header(const basic_header &h, const + extended_header &e) +{ + std::string ret; + + ret.push_back(char(h.b0)); + ret.push_back(char(h.b1)); + ret.append( + reinterpret_cast(e.bytes), + get_header_len(h)-BASIC_HEADER_LENGTH + ); + + return ret; +} + +/// Extract the masking key from a frame header +/** + * Note that while read and written as an integer at times, this value is not + * an integer and should never be interpreted as one. Big and little endian + * machines will generate and store masking keys differently without issue as + * long as the integer values remain irrelivant. + * + * @param h The basic header to extract from + * @param e The extended header to extract from + * + * @return The masking key as an integer. + */ +inline masking_key_type get_masking_key(const basic_header &h, const + extended_header &e) +{ + masking_key_type temp32; + + if (!get_masked(h)) { + temp32.i = 0; + } else { + unsigned int offset = get_masking_key_offset(h); + std::copy(e.bytes+offset,e.bytes+offset+4,temp32.c); + } + + return temp32; +} + +/// Extract the extended size field from an extended header +/** + * It is the responsibility of the caller to verify that e is a valid extended + * header. This function assumes that e contains an extended payload size. + * + * @param e The extended header to extract from + * + * @return The size encoded in the extended header in host byte order + */ +inline uint16_t get_extended_size(const extended_header &e) { + uint16_converter temp16; + std::copy(e.bytes,e.bytes+2,temp16.c); + return ntohs(temp16.i); +} + +/// Extract the jumbo size field from an extended header +/** + * It is the responsibility of the caller to verify that e is a valid extended + * header. This function assumes that e contains a jumbo payload size. + * + * @param e The extended header to extract from + * + * @return The size encoded in the extended header in host byte order + */ +inline uint64_t get_jumbo_size(const extended_header &e) { + uint64_converter temp64; + std::copy(e.bytes,e.bytes+8,temp64.c); + return lib::net::_ntohll(temp64.i); +} + +/// Extract the full payload size field from a WebSocket header +/** + * It is the responsibility of the caller to verify that h and e together + * represent a valid WebSocket frame header. This function assumes only that h + * and e are valid. It uses information in the basic header to determine where + * to look for the payload_size + * + * @param h The basic header to extract from + * @param e The extended header to extract from + * + * @return The size encoded in the combined header in host byte order. + */ +inline uint64_t get_payload_size(const basic_header &h, const + extended_header &e) +{ + uint8_t val = get_basic_size(h); + + if (val <= limits::payload_size_basic) { + return val; + } else if (val == payload_size_code_16bit) { + return get_extended_size(e); + } else { + return get_jumbo_size(e); + } +} + +/// Extract a masking key into a value the size of a machine word. +/** + * Machine word size must be 4 or 8. + * + * @param key Masking key to extract from + * + * @return prepared key as a machine word + */ +inline size_t prepare_masking_key(const masking_key_type& key) { + size_t low_bits = static_cast(key.i); + + if (sizeof(size_t) == 8) { + uint64_t high_bits = static_cast(key.i); + return static_cast((high_bits << 32) | low_bits); + } else { + return low_bits; + } +} + +/// circularly shifts the supplied prepared masking key by offset bytes +/** + * Prepared_key must be the output of prepare_masking_key with the associated + * restrictions on the machine word size. offset must be greater than or equal + * to zero and less than sizeof(size_t). + */ +inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) { + if (offset == 0) { + return prepared_key; + } + if (lib::net::is_little_endian()) { + size_t temp = prepared_key << (sizeof(size_t)-offset)*8; + return (prepared_key >> offset*8) | temp; + } else { + size_t temp = prepared_key >> (sizeof(size_t)-offset)*8; + return (prepared_key << offset*8) | temp; + } +} + +/// Byte by byte mask/unmask +/** + * Iterator based byte by byte masking and unmasking for WebSocket payloads. + * Performs masking in place using the supplied key offset by the supplied + * offset number of bytes. + * + * This function is simple and can be done in place on input with arbitrary + * lengths and does not vary based on machine word size. It is slow. + * + * @param b Beginning iterator to start masking + * + * @param e Ending iterator to end masking + * + * @param o Beginning iterator to store masked results + * + * @param key 32 bit key to mask with. + * + * @param key_offset offset value to start masking at. + */ +template +void byte_mask(input_iter first, input_iter last, output_iter result, + masking_key_type const & key, size_t key_offset) +{ + size_t key_index = key_offset%4; + while (first != last) { + *result = *first ^ key.c[key_index++]; + key_index %= 4; + ++result; + ++first; + } +} + +/// Byte by byte mask/unmask (in place) +/** + * Iterator based byte by byte masking and unmasking for WebSocket payloads. + * Performs masking in place using the supplied key offset by the supplied + * offset number of bytes. + * + * This function is simple and can be done in place on input with arbitrary + * lengths and does not vary based on machine word size. It is slow. + * + * @param b Beginning iterator to start masking + * + * @param e Ending iterator to end masking + * + * @param key 32 bit key to mask with. + * + * @param key_offset offset value to start masking at. + */ +template +void byte_mask(iter_type b, iter_type e, masking_key_type const & key, + size_t key_offset) +{ + byte_mask(b,e,b,key,key_offset); +} + +/// Exact word aligned mask/unmask +/** + * Balanced combination of byte by byte and circular word by word masking. + * Best used to mask complete messages at once. Has much higher setup costs than + * word_mask_circ but works with exact sized buffers. + * + * Buffer based word by word masking and unmasking for WebSocket payloads. + * Masking is done in word by word chunks with the remainder not divisible by + * the word size done byte by byte. + * + * input and output must both be at least length bytes. Exactly length bytes + * will be written. + * + * @param input buffer to mask or unmask + * + * @param output buffer to store the output. May be the same as input. + * + * @param length length of data buffer + * + * @param key Masking key to use + */ +inline void word_mask_exact(uint8_t* input, uint8_t* output, size_t length, + const masking_key_type& key) +{ + size_t prepared_key = prepare_masking_key(key); + size_t n = length/sizeof(size_t); + size_t* input_word = reinterpret_cast(input); + size_t* output_word = reinterpret_cast(output); + + for (size_t i = 0; i < n; i++) { + output_word[i] = input_word[i] ^ prepared_key; + } + + for (size_t i = n*sizeof(size_t); i < length; i++) { + output[i] = input[i] ^ key.c[i%4]; + } +} + +/// Exact word aligned mask/unmask (in place) +/** + * In place version of word_mask_exact + * + * @see word_mask_exact + * + * @param data buffer to read and write from + * + * @param length length of data buffer + * + * @param key Masking key to use + */ +inline void word_mask_exact(uint8_t* data, size_t length, const + masking_key_type& key) +{ + word_mask_exact(data,data,length,key); +} + +/// Circular word aligned mask/unmask +/** + * Performs a circular mask/unmask in word sized chunks using pre-prepared keys + * that store state between calls. Best for providing streaming masking or + * unmasking of small chunks at a time of a larger message. Requires that the + * underlying allocated size of the data buffer be a multiple of the word size. + * Data in the buffer after `length` will be overwritten only with the same + * values that were originally present. + * + * Buffer based word by word masking and unmasking for WebSocket payloads. + * Performs masking in place using the supplied key. Casts the data buffer to + * an array of size_t's and performs masking word by word. The underlying + * buffer size must be a muliple of the word size. + * + * word_mask returns a copy of prepared_key circularly shifted based on the + * length value. The returned value may be fed back into word_mask when more + * data is available. + * + * input and output must both have length at least: + * ceil(length/sizeof(size_t))*sizeof(size_t) + * Exactly that many bytes will be written, although only exactly length bytes + * will be changed (trailing bytes will be replaced without masking) + * + * @param data Character buffer to mask + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length, + size_t prepared_key) +{ + size_t n = length / sizeof(size_t); // whole words + size_t l = length - (n * sizeof(size_t)); // remaining bytes + size_t * input_word = reinterpret_cast(input); + size_t * output_word = reinterpret_cast(output); + + // mask word by word + for (size_t i = 0; i < n; i++) { + output_word[i] = input_word[i] ^ prepared_key; + } + + // mask partial word at the end + size_t start = length - l; + uint8_t * byte_key = reinterpret_cast(&prepared_key); + for (size_t i = 0; i < l; ++i) { + output[start+i] = input[start+i] ^ byte_key[i]; + } + + return circshift_prepared_key(prepared_key,l); +} + +/// Circular word aligned mask/unmask (in place) +/** + * In place version of word_mask_circ + * + * @see word_mask_circ + * + * @param data Character buffer to read from and write to + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t word_mask_circ(uint8_t* data, size_t length, size_t prepared_key){ + return word_mask_circ(data,data,length,prepared_key); +} + +/// Circular byte aligned mask/unmask +/** + * Performs a circular mask/unmask in byte sized chunks using pre-prepared keys + * that store state between calls. Best for providing streaming masking or + * unmasking of small chunks at a time of a larger message. Requires that the + * underlying allocated size of the data buffer be a multiple of the word size. + * Data in the buffer after `length` will be overwritten only with the same + * values that were originally present. + * + * word_mask returns a copy of prepared_key circularly shifted based on the + * length value. The returned value may be fed back into byte_mask when more + * data is available. + * + * @param data Character buffer to mask + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t byte_mask_circ(uint8_t * input, uint8_t * output, size_t length, + size_t prepared_key) +{ + uint32_converter key; + key.i = prepared_key; + + for (size_t i = 0; i < length; ++i) { + output[i] = input[i] ^ key.c[i % 4]; + } + + return circshift_prepared_key(prepared_key,length % 4); +} + +/// Circular byte aligned mask/unmask (in place) +/** + * In place version of byte_mask_circ + * + * @see byte_mask_circ + * + * @param data Character buffer to read from and write to + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t byte_mask_circ(uint8_t* data, size_t length, size_t prepared_key){ + return byte_mask_circ(data,data,length,prepared_key); +} + +} // namespace frame +} // namespace websocketpp + +#endif //WEBSOCKETPP_FRAME_HPP diff --git a/subprojects/websocketpp/websocketpp/http/constants.hpp b/subprojects/websocketpp/websocketpp/http/constants.hpp new file mode 100644 index 000000000..f946cb317 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/http/constants.hpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_CONSTANTS_HPP +#define HTTP_CONSTANTS_HPP + +#include +#include +#include +#include +#include + +namespace websocketpp { +/// HTTP handling support +namespace http { + /// The type of an HTTP attribute list + /** + * The attribute list is an unordered key/value map. Encoded attribute + * values are delimited by semicolons. + */ + typedef std::map attribute_list; + + /// The type of an HTTP parameter list + /** + * The parameter list is an ordered pairing of a parameter and its + * associated attribute list. Encoded parameter values are delimited by + * commas. + */ + typedef std::vector< std::pair > parameter_list; + + /// Literal value of the HTTP header delimiter + static char const header_delimiter[] = "\r\n"; + + /// Literal value of the HTTP header separator + static char const header_separator[] = ":"; + + /// Literal value of an empty header + static std::string const empty_header; + + /// Maximum size in bytes before rejecting an HTTP header as too big. + size_t const max_header_size = 16000; + + /// Default Maximum size in bytes for HTTP message bodies. + size_t const max_body_size = 32000000; + + /// Number of bytes to use for temporary istream read buffers + size_t const istream_buffer = 512; + + /// invalid HTTP token characters + /** + * 0x00 - 0x32, 0x7f-0xff + * ( ) < > @ , ; : \ " / [ ] ? = { } + */ + static char const header_token[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..0f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 10..1f + 0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0, // 20..2f + 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 30..3f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 40..4f + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, // 50..5f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 60..6f + 1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0, // 70..7f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 80..8f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 90..9f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // a0..af + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // b0..bf + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0..cf + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0..df + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0..ef + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // f0..ff + }; + + /// Is the character a token + inline bool is_token_char(unsigned char c) { + return (header_token[c] == 1); + } + + /// Is the character a non-token + inline bool is_not_token_char(unsigned char c) { + return !header_token[c]; + } + + /// Is the character whitespace + /** + * whitespace is space (32) or horizontal tab (9) + */ + inline bool is_whitespace_char(unsigned char c) { + return (c == 9 || c == 32); + } + + /// Is the character non-whitespace + inline bool is_not_whitespace_char(unsigned char c) { + return (c != 9 && c != 32); + } + + /// HTTP Status codes + namespace status_code { + enum value { + uninitialized = 0, + + continue_code = 100, + switching_protocols = 101, + + ok = 200, + created = 201, + accepted = 202, + non_authoritative_information = 203, + no_content = 204, + reset_content = 205, + partial_content = 206, + + multiple_choices = 300, + moved_permanently = 301, + found = 302, + see_other = 303, + not_modified = 304, + use_proxy = 305, + temporary_redirect = 307, + + bad_request = 400, + unauthorized = 401, + payment_required = 402, + forbidden = 403, + not_found = 404, + method_not_allowed = 405, + not_acceptable = 406, + proxy_authentication_required = 407, + request_timeout = 408, + conflict = 409, + gone = 410, + length_required = 411, + precondition_failed = 412, + request_entity_too_large = 413, + request_uri_too_long = 414, + unsupported_media_type = 415, + request_range_not_satisfiable = 416, + expectation_failed = 417, + im_a_teapot = 418, + upgrade_required = 426, + precondition_required = 428, + too_many_requests = 429, + request_header_fields_too_large = 431, + + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503, + gateway_timeout = 504, + http_version_not_supported = 505, + not_extended = 510, + network_authentication_required = 511 + }; + + // TODO: should this be inline? + inline std::string get_string(value c) { + switch (c) { + case uninitialized: + return "Uninitialized"; + case continue_code: + return "Continue"; + case switching_protocols: + return "Switching Protocols"; + case ok: + return "OK"; + case created: + return "Created"; + case accepted: + return "Accepted"; + case non_authoritative_information: + return "Non Authoritative Information"; + case no_content: + return "No Content"; + case reset_content: + return "Reset Content"; + case partial_content: + return "Partial Content"; + case multiple_choices: + return "Multiple Choices"; + case moved_permanently: + return "Moved Permanently"; + case found: + return "Found"; + case see_other: + return "See Other"; + case not_modified: + return "Not Modified"; + case use_proxy: + return "Use Proxy"; + case temporary_redirect: + return "Temporary Redirect"; + case bad_request: + return "Bad Request"; + case unauthorized: + return "Unauthorized"; + case payment_required: + return "Payment Required"; + case forbidden: + return "Forbidden"; + case not_found: + return "Not Found"; + case method_not_allowed: + return "Method Not Allowed"; + case not_acceptable: + return "Not Acceptable"; + case proxy_authentication_required: + return "Proxy Authentication Required"; + case request_timeout: + return "Request Timeout"; + case conflict: + return "Conflict"; + case gone: + return "Gone"; + case length_required: + return "Length Required"; + case precondition_failed: + return "Precondition Failed"; + case request_entity_too_large: + return "Request Entity Too Large"; + case request_uri_too_long: + return "Request-URI Too Long"; + case unsupported_media_type: + return "Unsupported Media Type"; + case request_range_not_satisfiable: + return "Requested Range Not Satisfiable"; + case expectation_failed: + return "Expectation Failed"; + case im_a_teapot: + return "I'm a teapot"; + case upgrade_required: + return "Upgrade Required"; + case precondition_required: + return "Precondition Required"; + case too_many_requests: + return "Too Many Requests"; + case request_header_fields_too_large: + return "Request Header Fields Too Large"; + case internal_server_error: + return "Internal Server Error"; + case not_implemented: + return "Not Implemented"; + case bad_gateway: + return "Bad Gateway"; + case service_unavailable: + return "Service Unavailable"; + case gateway_timeout: + return "Gateway Timeout"; + case http_version_not_supported: + return "HTTP Version Not Supported"; + case not_extended: + return "Not Extended"; + case network_authentication_required: + return "Network Authentication Required"; + default: + return "Unknown"; + } + } + } + + class exception : public std::exception { + public: + exception(const std::string& log_msg, + status_code::value error_code, + const std::string& error_msg = std::string(), + const std::string& body = std::string()) + : m_msg(log_msg) + , m_error_msg(error_msg) + , m_body(body) + , m_error_code(error_code) {} + + ~exception() throw() {} + + virtual const char* what() const throw() { + return m_msg.c_str(); + } + + std::string m_msg; + std::string m_error_msg; + std::string m_body; + status_code::value m_error_code; + }; +} +} + +#endif // HTTP_CONSTANTS_HPP diff --git a/subprojects/websocketpp/websocketpp/http/impl/parser.hpp b/subprojects/websocketpp/websocketpp/http/impl/parser.hpp new file mode 100644 index 000000000..fd24adb5d --- /dev/null +++ b/subprojects/websocketpp/websocketpp/http/impl/parser.hpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_IMPL_HPP +#define HTTP_PARSER_IMPL_HPP + +#include +#include +#include +#include +#include + +namespace websocketpp { +namespace http { +namespace parser { + +inline void parser::set_version(std::string const & version) { + m_version = version; +} + +inline std::string const & parser::get_header(std::string const & key) const { + header_list::const_iterator h = m_headers.find(key); + + if (h == m_headers.end()) { + return empty_header; + } else { + return h->second; + } +} + +inline bool parser::get_header_as_plist(std::string const & key, + parameter_list & out) const +{ + header_list::const_iterator it = m_headers.find(key); + + if (it == m_headers.end() || it->second.size() == 0) { + return false; + } + + return this->parse_parameter_list(it->second,out); +} + +inline void parser::append_header(std::string const & key, std::string const & + val) +{ + if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) { + throw exception("Invalid header name",status_code::bad_request); + } + + if (this->get_header(key).empty()) { + m_headers[key] = val; + } else { + m_headers[key] += ", " + val; + } +} + +inline void parser::replace_header(std::string const & key, std::string const & + val) +{ + m_headers[key] = val; +} + +inline void parser::remove_header(std::string const & key) { + m_headers.erase(key); +} + +inline void parser::set_body(std::string const & value) { + if (value.size() == 0) { + remove_header("Content-Length"); + m_body.clear(); + return; + } + + // TODO: should this method respect the max size? If so how should errors + // be indicated? + + std::stringstream len; + len << value.size(); + replace_header("Content-Length", len.str()); + m_body = value; +} + +inline bool parser::parse_parameter_list(std::string const & in, + parameter_list & out) const +{ + if (in.size() == 0) { + return false; + } + + std::string::const_iterator it; + it = extract_parameters(in.begin(),in.end(),out); + return (it == in.begin()); +} + +inline bool parser::prepare_body() { + if (!get_header("Content-Length").empty()) { + std::string const & cl_header = get_header("Content-Length"); + char * end; + + // TODO: not 100% sure what the compatibility of this method is. Also, + // I believe this will only work up to 32bit sizes. Is there a need for + // > 4GiB HTTP payloads? + m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); + + if (m_body_bytes_needed > m_body_bytes_max) { + throw exception("HTTP message body too large", + status_code::request_entity_too_large); + } + + m_body_encoding = body_encoding::plain; + return true; + } else if (get_header("Transfer-Encoding") == "chunked") { + // TODO + //m_body_encoding = body_encoding::chunked; + return false; + } else { + return false; + } +} + +inline size_t parser::process_body(char const * buf, size_t len) { + if (m_body_encoding == body_encoding::plain) { + size_t processed = (std::min)(m_body_bytes_needed,len); + m_body.append(buf,processed); + m_body_bytes_needed -= processed; + return processed; + } else if (m_body_encoding == body_encoding::chunked) { + // TODO: + throw exception("Unexpected body encoding", + status_code::internal_server_error); + } else { + throw exception("Unexpected body encoding", + status_code::internal_server_error); + } +} + +inline void parser::process_header(std::string::iterator begin, + std::string::iterator end) +{ + std::string::iterator cursor = std::search( + begin, + end, + header_separator, + header_separator + sizeof(header_separator) - 1 + ); + + if (cursor == end) { + throw exception("Invalid header line",status_code::bad_request); + } + + append_header(strip_lws(std::string(begin,cursor)), + strip_lws(std::string(cursor+sizeof(header_separator)-1,end))); +} + +inline header_list const & parser::get_headers() const { + return m_headers; +} + +inline std::string parser::raw_headers() const { + std::stringstream raw; + + header_list::const_iterator it; + for (it = m_headers.begin(); it != m_headers.end(); it++) { + raw << it->first << ": " << it->second << "\r\n"; + } + + return raw.str(); +} + + + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#endif // HTTP_PARSER_IMPL_HPP diff --git a/subprojects/websocketpp/websocketpp/http/impl/request.hpp b/subprojects/websocketpp/websocketpp/http/impl/request.hpp new file mode 100644 index 000000000..311a620f6 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/http/impl/request.hpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_REQUEST_IMPL_HPP +#define HTTP_PARSER_REQUEST_IMPL_HPP + +#include +#include +#include + +#include + +namespace websocketpp { +namespace http { +namespace parser { + +inline size_t request::consume(char const * buf, size_t len) { + size_t bytes_processed; + + if (m_ready) {return 0;} + + if (m_body_bytes_needed > 0) { + bytes_processed = process_body(buf,len); + if (body_ready()) { + m_ready = true; + } + return bytes_processed; + } + + // copy new header bytes into buffer + m_buf->append(buf,len); + + // Search for delimiter in buf. If found read until then. If not read all + std::string::iterator begin = m_buf->begin(); + std::string::iterator end; + + for (;;) { + // search for line delimiter + end = std::search( + begin, + m_buf->end(), + header_delimiter, + header_delimiter+sizeof(header_delimiter)-1 + ); + + m_header_bytes += (end-begin+sizeof(header_delimiter)); + + if (m_header_bytes > max_header_size) { + // exceeded max header size + throw exception("Maximum header size exceeded.", + status_code::request_header_fields_too_large); + } + + if (end == m_buf->end()) { + // we are out of bytes. Discard the processed bytes and copy the + // remaining unprecessed bytes to the beginning of the buffer + std::copy(begin,end,m_buf->begin()); + m_buf->resize(static_cast(end-begin)); + m_header_bytes -= m_buf->size(); + + return len; + } + + //the range [begin,end) now represents a line to be processed. + if (end-begin == 0) { + // we got a blank line + if (m_method.empty() || get_header("Host").empty()) { + throw exception("Incomplete Request",status_code::bad_request); + } + + bytes_processed = ( + len - static_cast(m_buf->end()-end) + + sizeof(header_delimiter) - 1 + ); + + // frees memory used temporarily during request parsing + m_buf.reset(); + + // if this was not an upgrade request and has a content length + // continue capturing content-length bytes and expose them as a + // request body. + + if (prepare_body()) { + bytes_processed += process_body(buf+bytes_processed,len-bytes_processed); + if (body_ready()) { + m_ready = true; + } + return bytes_processed; + } else { + m_ready = true; + + // return number of bytes processed (starting bytes - bytes left) + return bytes_processed; + } + } else { + if (m_method.empty()) { + this->process(begin,end); + } else { + this->process_header(begin,end); + } + } + + begin = end+(sizeof(header_delimiter)-1); + } +} + +inline std::string request::raw() const { + // TODO: validation. Make sure all required fields have been set? + std::stringstream ret; + + ret << m_method << " " << m_uri << " " << get_version() << "\r\n"; + ret << raw_headers() << "\r\n" << m_body; + + return ret.str(); +} + +inline std::string request::raw_head() const { + // TODO: validation. Make sure all required fields have been set? + std::stringstream ret; + + ret << m_method << " " << m_uri << " " << get_version() << "\r\n"; + ret << raw_headers() << "\r\n"; + + return ret.str(); +} + +inline void request::set_method(std::string const & method) { + if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) { + throw exception("Invalid method token.",status_code::bad_request); + } + + m_method = method; +} + +inline void request::set_uri(std::string const & uri) { + // TODO: validation? + m_uri = uri; +} + +inline void request::process(std::string::iterator begin, std::string::iterator + end) +{ + std::string::iterator cursor_start = begin; + std::string::iterator cursor_end = std::find(begin,end,' '); + + if (cursor_end == end) { + throw exception("Invalid request line1",status_code::bad_request); + } + + set_method(std::string(cursor_start,cursor_end)); + + cursor_start = cursor_end+1; + cursor_end = std::find(cursor_start,end,' '); + + if (cursor_end == end) { + throw exception("Invalid request line2",status_code::bad_request); + } + + set_uri(std::string(cursor_start,cursor_end)); + set_version(std::string(cursor_end+1,end)); +} + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#endif // HTTP_PARSER_REQUEST_IMPL_HPP diff --git a/subprojects/websocketpp/websocketpp/http/impl/response.hpp b/subprojects/websocketpp/websocketpp/http/impl/response.hpp new file mode 100644 index 000000000..4400cda5c --- /dev/null +++ b/subprojects/websocketpp/websocketpp/http/impl/response.hpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_RESPONSE_IMPL_HPP +#define HTTP_PARSER_RESPONSE_IMPL_HPP + +#include +#include +#include +#include + +#include + +namespace websocketpp { +namespace http { +namespace parser { + +inline size_t response::consume(char const * buf, size_t len) { + if (m_state == DONE) {return 0;} + + if (m_state == BODY) { + return this->process_body(buf,len); + } + + // copy new header bytes into buffer + m_buf->append(buf,len); + + // Search for delimiter in buf. If found read until then. If not read all + std::string::iterator begin = m_buf->begin(); + std::string::iterator end = begin; + + + for (;;) { + // search for delimiter + end = std::search( + begin, + m_buf->end(), + header_delimiter, + header_delimiter + sizeof(header_delimiter) - 1 + ); + + m_header_bytes += (end-begin+sizeof(header_delimiter)); + + if (m_header_bytes > max_header_size) { + // exceeded max header size + throw exception("Maximum header size exceeded.", + status_code::request_header_fields_too_large); + } + + if (end == m_buf->end()) { + // we are out of bytes. Discard the processed bytes and copy the + // remaining unprecessed bytes to the beginning of the buffer + std::copy(begin,end,m_buf->begin()); + m_buf->resize(static_cast(end-begin)); + + m_read += len; + m_header_bytes -= m_buf->size(); + + return len; + } + + //the range [begin,end) now represents a line to be processed. + + if (end-begin == 0) { + // we got a blank line + if (m_state == RESPONSE_LINE) { + throw exception("Incomplete Request",status_code::bad_request); + } + + // TODO: grab content-length + std::string length = get_header("Content-Length"); + + if (length.empty()) { + // no content length found, read indefinitely + m_read = 0; + } else { + std::istringstream ss(length); + + if ((ss >> m_read).fail()) { + throw exception("Unable to parse Content-Length header", + status_code::bad_request); + } + } + + m_state = BODY; + + // calc header bytes processed (starting bytes - bytes left) + size_t read = ( + len - static_cast(m_buf->end() - end) + + sizeof(header_delimiter) - 1 + ); + + // if there were bytes left process them as body bytes + if (read < len) { + read += this->process_body(buf+read,(len-read)); + } + + // frees memory used temporarily during header parsing + m_buf.reset(); + + return read; + } else { + if (m_state == RESPONSE_LINE) { + this->process(begin,end); + m_state = HEADERS; + } else { + this->process_header(begin,end); + } + } + + begin = end+(sizeof(header_delimiter) - 1); + } +} + +inline size_t response::consume(std::istream & s) { + char buf[istream_buffer]; + size_t bytes_read; + size_t bytes_processed; + size_t total = 0; + + while (s.good()) { + s.getline(buf,istream_buffer); + bytes_read = static_cast(s.gcount()); + + if (s.fail() || s.eof()) { + bytes_processed = this->consume(buf,bytes_read); + total += bytes_processed; + + if (bytes_processed != bytes_read) { + // problem + break; + } + } else if (s.bad()) { + // problem + break; + } else { + // the delimiting newline was found. Replace the trailing null with + // the newline that was discarded, since our raw consume function + // expects the newline to be be there. + buf[bytes_read-1] = '\n'; + bytes_processed = this->consume(buf,bytes_read); + total += bytes_processed; + + if (bytes_processed != bytes_read) { + // problem + break; + } + } + } + + return total; +} + +inline std::string response::raw() const { + // TODO: validation. Make sure all required fields have been set? + + std::stringstream ret; + + ret << get_version() << " " << m_status_code << " " << m_status_msg; + ret << "\r\n" << raw_headers() << "\r\n"; + + ret << m_body; + + return ret.str(); +} + +inline void response::set_status(status_code::value code) { + // TODO: validation? + m_status_code = code; + m_status_msg = get_string(code); +} + +inline void response::set_status(status_code::value code, std::string const & + msg) +{ + // TODO: validation? + m_status_code = code; + m_status_msg = msg; +} + +inline void response::process(std::string::iterator begin, + std::string::iterator end) +{ + std::string::iterator cursor_start = begin; + std::string::iterator cursor_end = std::find(begin,end,' '); + + if (cursor_end == end) { + throw exception("Invalid response line",status_code::bad_request); + } + + set_version(std::string(cursor_start,cursor_end)); + + cursor_start = cursor_end+1; + cursor_end = std::find(cursor_start,end,' '); + + if (cursor_end == end) { + throw exception("Invalid request line",status_code::bad_request); + } + + int code; + + std::istringstream ss(std::string(cursor_start,cursor_end)); + + if ((ss >> code).fail()) { + throw exception("Unable to parse response code",status_code::bad_request); + } + + set_status(status_code::value(code),std::string(cursor_end+1,end)); +} + +inline size_t response::process_body(char const * buf, size_t len) { + // If no content length was set then we read forever and never set m_ready + if (m_read == 0) { + //m_body.append(buf,len); + //return len; + m_state = DONE; + return 0; + } + + // Otherwise m_read is the number of bytes left. + size_t to_read; + + if (len >= m_read) { + // if we have more bytes than we need read, read only the amount needed + // then set done state + to_read = m_read; + m_state = DONE; + } else { + // we need more bytes than are available, read them all + to_read = len; + } + + m_body.append(buf,to_read); + m_read -= to_read; + return to_read; +} + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#endif // HTTP_PARSER_RESPONSE_IMPL_HPP diff --git a/subprojects/websocketpp/websocketpp/http/parser.hpp b/subprojects/websocketpp/websocketpp/http/parser.hpp new file mode 100644 index 000000000..9d309ec91 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/http/parser.hpp @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_HPP +#define HTTP_PARSER_HPP + +#include +#include +#include +#include + +#include +#include + +namespace websocketpp { +namespace http { +namespace parser { + +namespace state { + enum value { + method, + resource, + version, + headers + }; +} + +namespace body_encoding { + enum value { + unknown, + plain, + chunked + }; +} + +typedef std::map header_list; + +/// Read and return the next token in the stream +/** + * Read until a non-token character is found and then return the token and + * iterator to the next character to read + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return A pair containing the token and an iterator to the next character in + * the stream + */ +template +std::pair extract_token(InputIterator begin, + InputIterator end) +{ + InputIterator it = std::find_if(begin,end,&is_not_token_char); + return std::make_pair(std::string(begin,it),it); +} + +/// Read and return the next quoted string in the stream +/** + * Read a double quoted string starting at `begin`. The quotes themselves are + * stripped. The quoted value is returned along with an iterator to the next + * character to read + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return A pair containing the string read and an iterator to the next + * character in the stream + */ +template +std::pair extract_quoted_string(InputIterator begin, + InputIterator end) +{ + std::string s; + + if (end == begin) { + return std::make_pair(s,begin); + } + + if (*begin != '"') { + return std::make_pair(s,begin); + } + + InputIterator cursor = begin+1; + InputIterator marker = cursor; + + cursor = std::find(cursor,end,'"'); + + while (cursor != end) { + // either this is the end or a quoted string + if (*(cursor-1) == '\\') { + s.append(marker,cursor-1); + s.append(1,'"'); + ++cursor; + marker = cursor; + } else { + s.append(marker,cursor); + ++cursor; + return std::make_pair(s,cursor); + } + + cursor = std::find(cursor,end,'"'); + } + + return std::make_pair("",begin); +} + +/// Read and discard one unit of linear whitespace +/** + * Read one unit of linear white space and return the iterator to the character + * afterwards. If `begin` is returned, no whitespace was extracted. + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return An iterator to the character after the linear whitespace read + */ +template +InputIterator extract_lws(InputIterator begin, InputIterator end) { + InputIterator it = begin; + + // strip leading CRLF + if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' && + is_whitespace_char(static_cast(*(begin+2)))) + { + it+=3; + } + + it = std::find_if(it,end,&is_not_whitespace_char); + return it; +} + +/// Read and discard linear whitespace +/** + * Read linear white space until a non-lws character is read and return an + * iterator to that character. If `begin` is returned, no whitespace was + * extracted. + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return An iterator to the character after the linear whitespace read + */ +template +InputIterator extract_all_lws(InputIterator begin, InputIterator end) { + InputIterator old_it; + InputIterator new_it = begin; + + do { + // Pull value from previous iteration + old_it = new_it; + + // look ahead another pass + new_it = extract_lws(old_it,end); + } while (new_it != end && old_it != new_it); + + return new_it; +} + +/// Extract HTTP attributes +/** + * An http attributes list is a semicolon delimited list of key value pairs in + * the format: *( ";" attribute "=" value ) where attribute is a token and value + * is a token or quoted string. + * + * Attributes extracted are appended to the supplied attributes list + * `attributes`. + * + * @param [in] begin An iterator to the beginning of the sequence + * @param [in] end An iterator to the end of the sequence + * @param [out] attributes A reference to the attributes list to append + * attribute/value pairs extracted to + * @return An iterator to the character after the last atribute read + */ +template +InputIterator extract_attributes(InputIterator begin, InputIterator end, + attribute_list & attributes) +{ + InputIterator cursor; + bool first = true; + + if (begin == end) { + return begin; + } + + cursor = begin; + std::pair ret; + + while (cursor != end) { + std::string name; + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) { + break; + } + + if (first) { + // ignore this check for the very first pass + first = false; + } else { + if (*cursor == ';') { + // advance past the ';' + ++cursor; + } else { + // non-semicolon in this position indicates end end of the + // attribute list, break and return. + break; + } + } + + cursor = http::parser::extract_all_lws(cursor,end); + ret = http::parser::extract_token(cursor,end); + + if (ret.first.empty()) { + // error: expected a token + return begin; + } else { + name = ret.first; + cursor = ret.second; + } + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end || *cursor != '=') { + // if there is an equals sign, read the attribute value. Otherwise + // record a blank value and continue + attributes[name].clear(); + continue; + } + + // advance past the '=' + ++cursor; + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) { + // error: expected a token or quoted string + return begin; + } + + ret = http::parser::extract_quoted_string(cursor,end); + if (ret.second != cursor) { + attributes[name] = ret.first; + cursor = ret.second; + continue; + } + + ret = http::parser::extract_token(cursor,end); + if (ret.first.empty()) { + // error : expected token or quoted string + return begin; + } else { + attributes[name] = ret.first; + cursor = ret.second; + } + } + + return cursor; +} + +/// Extract HTTP parameters +/** + * An http parameters list is a comma delimited list of tokens followed by + * optional semicolon delimited attributes lists. + * + * Parameters extracted are appended to the supplied parameters list + * `parameters`. + * + * @param [in] begin An iterator to the beginning of the sequence + * @param [in] end An iterator to the end of the sequence + * @param [out] parameters A reference to the parameters list to append + * paramter values extracted to + * @return An iterator to the character after the last parameter read + */ +template +InputIterator extract_parameters(InputIterator begin, InputIterator end, + parameter_list ¶meters) +{ + InputIterator cursor; + + if (begin == end) { + // error: expected non-zero length range + return begin; + } + + cursor = begin; + std::pair ret; + + /** + * LWS + * token + * LWS + * *(";" method-param) + * LWS + * ,=loop again + */ + while (cursor != end) { + std::string parameter_name; + attribute_list attributes; + + // extract any stray whitespace + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) {break;} + + ret = http::parser::extract_token(cursor,end); + + if (ret.first.empty()) { + // error: expected a token + return begin; + } else { + parameter_name = ret.first; + cursor = ret.second; + } + + // Safe break point, insert parameter with blank attributes and exit + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) { + //parameters[parameter_name] = attributes; + parameters.push_back(std::make_pair(parameter_name,attributes)); + break; + } + + // If there is an attribute list, read it in + if (*cursor == ';') { + InputIterator acursor; + + ++cursor; + acursor = http::parser::extract_attributes(cursor,end,attributes); + + if (acursor == cursor) { + // attribute extraction ended in syntax error + return begin; + } + + cursor = acursor; + } + + // insert parameter into output list + //parameters[parameter_name] = attributes; + parameters.push_back(std::make_pair(parameter_name,attributes)); + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) {break;} + + // if next char is ',' then read another parameter, else stop + if (*cursor != ',') { + break; + } + + // advance past comma + ++cursor; + + if (cursor == end) { + // expected more bytes after a comma + return begin; + } + } + + return cursor; +} + +inline std::string strip_lws(std::string const & input) { + std::string::const_iterator begin = extract_all_lws(input.begin(),input.end()); + if (begin == input.end()) { + return std::string(); + } + + std::string::const_reverse_iterator rbegin = extract_all_lws(input.rbegin(),input.rend()); + if (rbegin == input.rend()) { + return std::string(); + } + + return std::string(begin,rbegin.base()); +} + +/// Base HTTP parser +/** + * Includes methods and data elements common to all types of HTTP messages such + * as headers, versions, bodies, etc. + */ +class parser { +public: + parser() + : m_header_bytes(0) + , m_body_bytes_needed(0) + , m_body_bytes_max(max_body_size) + , m_body_encoding(body_encoding::unknown) {} + + /// Get the HTTP version string + /** + * @return The version string for this parser + */ + std::string const & get_version() const { + return m_version; + } + + /// Set HTTP parser Version + /** + * Input should be in format: HTTP/x.y where x and y are positive integers. + * @todo Does this method need any validation? + * + * @param [in] version The value to set the HTTP version to. + */ + void set_version(std::string const & version); + + /// Get the value of an HTTP header + /** + * @todo Make this method case insensitive. + * + * @param [in] key The name/key of the header to get. + * @return The value associated with the given HTTP header key. + */ + std::string const & get_header(std::string const & key) const; + + /// Extract an HTTP parameter list from a parser header. + /** + * If the header requested doesn't exist or exists and is empty the + * parameter list is valid (but empty). + * + * @param [in] key The name/key of the HTTP header to use as input. + * @param [out] out The parameter list to store extracted parameters in. + * @return Whether or not the input was a valid parameter list. + */ + bool get_header_as_plist(std::string const & key, parameter_list & out) + const; + + /// Return a list of all HTTP headers + /** + * Return a list of all HTTP headers + * + * @since 0.8.0 + * + * @return A list of all HTTP headers + */ + header_list const & get_headers() const; + + /// Append a value to an existing HTTP header + /** + * This method will set the value of the HTTP header `key` with the + * indicated value. If a header with the name `key` already exists, `val` + * will be appended to the existing value. + * + * @todo Make this method case insensitive. + * @todo Should there be any restrictions on which keys are allowed? + * @todo Exception free varient + * + * @see replace_header + * + * @param [in] key The name/key of the header to append to. + * @param [in] val The value to append. + */ + void append_header(std::string const & key, std::string const & val); + + /// Set a value for an HTTP header, replacing an existing value + /** + * This method will set the value of the HTTP header `key` with the + * indicated value. If a header with the name `key` already exists, `val` + * will replace the existing value. + * + * @todo Make this method case insensitive. + * @todo Should there be any restrictions on which keys are allowed? + * @todo Exception free varient + * + * @see append_header + * + * @param [in] key The name/key of the header to append to. + * @param [in] val The value to append. + */ + void replace_header(std::string const & key, std::string const & val); + + /// Remove a header from the parser + /** + * Removes the header entirely from the parser. This is different than + * setting the value of the header to blank. + * + * @todo Make this method case insensitive. + * + * @param [in] key The name/key of the header to remove. + */ + void remove_header(std::string const & key); + + /// Get HTTP body + /** + * Gets the body of the HTTP object + * + * @return The body of the HTTP message. + */ + std::string const & get_body() const { + return m_body; + } + + /// Set body content + /** + * Set the body content of the HTTP response to the parameter string. Note + * set_body will also set the Content-Length HTTP header to the appropriate + * value. If you want the Content-Length header to be something else, do so + * via replace_header("Content-Length") after calling set_body() + * + * @param value String data to include as the body content. + */ + void set_body(std::string const & value); + + /// Get body size limit + /** + * Retrieves the maximum number of bytes to parse & buffer before canceling + * a request. + * + * @since 0.5.0 + * + * @return The maximum length of a message body. + */ + size_t get_max_body_size() const { + return m_body_bytes_max; + } + + /// Set body size limit + /** + * Set the maximum number of bytes to parse and buffer before canceling a + * request. + * + * @since 0.5.0 + * + * @param value The size to set the max body length to. + */ + void set_max_body_size(size_t value) { + m_body_bytes_max = value; + } + + /// Extract an HTTP parameter list from a string. + /** + * @param [in] in The input string. + * @param [out] out The parameter list to store extracted parameters in. + * @return Whether or not the input was a valid parameter list. + */ + bool parse_parameter_list(std::string const & in, parameter_list & out) + const; +protected: + /// Process a header line + /** + * @todo Update this method to be exception free. + * + * @param [in] begin An iterator to the beginning of the sequence. + * @param [in] end An iterator to the end of the sequence. + */ + void process_header(std::string::iterator begin, std::string::iterator end); + + /// Prepare the parser to begin parsing body data + /** + * Inspects headers to determine if the message has a body that needs to be + * read. If so, sets up the necessary state, otherwise returns false. If + * this method returns true and loading the message body is desired call + * `process_body` until it returns zero bytes or an error. + * + * Must not be called until after all headers have been processed. + * + * @since 0.5.0 + * + * @return True if more bytes are needed to load the body, false otherwise. + */ + bool prepare_body(); + + /// Process body data + /** + * Parses body data. + * + * @since 0.5.0 + * + * @param [in] begin An iterator to the beginning of the sequence. + * @param [in] end An iterator to the end of the sequence. + * @return The number of bytes processed + */ + size_t process_body(char const * buf, size_t len); + + /// Check if the parser is done parsing the body + /** + * Behavior before a call to `prepare_body` is undefined. + * + * @since 0.5.0 + * + * @return True if the message body has been completed loaded. + */ + bool body_ready() const { + return (m_body_bytes_needed == 0); + } + + /// Generate and return the HTTP headers as a string + /** + * Each headers will be followed by the \r\n sequence including the last one. + * A second \r\n sequence (blank header) is not appended by this method + * + * @return The HTTP headers as a string. + */ + std::string raw_headers() const; + + std::string m_version; + header_list m_headers; + + size_t m_header_bytes; + + std::string m_body; + size_t m_body_bytes_needed; + size_t m_body_bytes_max; + body_encoding::value m_body_encoding; +}; + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#include + +#endif // HTTP_PARSER_HPP diff --git a/subprojects/websocketpp/websocketpp/http/request.hpp b/subprojects/websocketpp/websocketpp/http/request.hpp new file mode 100644 index 000000000..3355c99b8 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/http/request.hpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_REQUEST_HPP +#define HTTP_PARSER_REQUEST_HPP + +#include + +#include +#include + +namespace websocketpp { +namespace http { +namespace parser { + +/// Stores, parses, and manipulates HTTP requests +/** + * http::request provides the following functionality for working with HTTP + * requests. + * + * - Initialize request via manually setting each element + * - Initialize request via reading raw bytes and parsing + * - Once initialized, access individual parsed elements + * - Once initialized, read entire request as raw bytes + */ +class request : public parser { +public: + typedef request type; + typedef lib::shared_ptr ptr; + + request() + : m_buf(lib::make_shared()) + , m_ready(false) {} + + /// Process bytes in the input buffer + /** + * Process up to len bytes from input buffer buf. Returns the number of + * bytes processed. Bytes left unprocessed means bytes left over after the + * final header delimiters. + * + * Consume is a streaming processor. It may be called multiple times on one + * request and the full headers need not be available before processing can + * begin. If the end of the request was reached during this call to consume + * the ready flag will be set. Further calls to consume once ready will be + * ignored. + * + * Consume will throw an http::exception in the case of an error. Typical + * error reasons include malformed requests, incomplete requests, and max + * header size being reached. + * + * @param buf Pointer to byte buffer + * @param len Size of byte buffer + * @return Number of bytes processed. + */ + size_t consume(char const * buf, size_t len); + + /// Returns whether or not the request is ready for reading. + bool ready() const { + return m_ready; + } + + /// Returns the full raw request (including the body) + std::string raw() const; + + /// Returns the raw request headers only (similar to an HTTP HEAD request) + std::string raw_head() const; + + /// Set the HTTP method. Must be a valid HTTP token + void set_method(std::string const & method); + + /// Return the request method + std::string const & get_method() const { + return m_method; + } + + /// Set the HTTP uri. Must be a valid HTTP uri + void set_uri(std::string const & uri); + + /// Return the requested URI + std::string const & get_uri() const { + return m_uri; + } + +private: + /// Helper function for message::consume. Process request line + void process(std::string::iterator begin, std::string::iterator end); + + lib::shared_ptr m_buf; + std::string m_method; + std::string m_uri; + bool m_ready; +}; + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#include + +#endif // HTTP_PARSER_REQUEST_HPP diff --git a/subprojects/websocketpp/websocketpp/http/response.hpp b/subprojects/websocketpp/websocketpp/http/response.hpp new file mode 100644 index 000000000..e724a3d3a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/http/response.hpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_RESPONSE_HPP +#define HTTP_PARSER_RESPONSE_HPP + +#include +#include + +#include + +namespace websocketpp { +namespace http { +namespace parser { + +/// Stores, parses, and manipulates HTTP responses +/** + * http::response provides the following functionality for working with HTTP + * responses. + * + * - Initialize response via manually setting each element + * - Initialize response via reading raw bytes and parsing + * - Once initialized, access individual parsed elements + * - Once initialized, read entire response as raw bytes + * + * http::response checks for header completeness separately from the full + * response. Once the header is complete, the Content-Length header is read to + * determine when to stop reading body bytes. If no Content-Length is present + * ready() will never return true. It is the responsibility of the caller to + * consume to determine when the response is complete (ie when the connection + * terminates, or some other metric). + */ +class response : public parser { +public: + typedef response type; + typedef lib::shared_ptr ptr; + + response() + : m_read(0) + , m_buf(lib::make_shared()) + , m_status_code(status_code::uninitialized) + , m_state(RESPONSE_LINE) {} + + /// Process bytes in the input buffer + /** + * Process up to len bytes from input buffer buf. Returns the number of + * bytes processed. Bytes left unprocessed means bytes left over after the + * final header delimiters. + * + * Consume is a streaming processor. It may be called multiple times on one + * response and the full headers need not be available before processing can + * begin. If the end of the response was reached during this call to consume + * the ready flag will be set. Further calls to consume once ready will be + * ignored. + * + * Consume will throw an http::exception in the case of an error. Typical + * error reasons include malformed responses, incomplete responses, and max + * header size being reached. + * + * @param buf Pointer to byte buffer + * @param len Size of byte buffer + * @return Number of bytes processed. + */ + size_t consume(char const * buf, size_t len); + + /// Process bytes in the input buffer (istream version) + /** + * Process bytes from istream s. Returns the number of bytes processed. + * Bytes left unprocessed means bytes left over after the final header + * delimiters. + * + * Consume is a streaming processor. It may be called multiple times on one + * response and the full headers need not be available before processing can + * begin. If the end of the response was reached during this call to consume + * the ready flag will be set. Further calls to consume once ready will be + * ignored. + * + * Consume will throw an http::exception in the case of an error. Typical + * error reasons include malformed responses, incomplete responses, and max + * header size being reached. + * + * @param buf Pointer to byte buffer + * @param len Size of byte buffer + * @return Number of bytes processed. + */ + size_t consume(std::istream & s); + + /// Returns true if the response is ready. + /** + * @note will never return true if the content length header is not present + */ + bool ready() const { + return m_state == DONE; + } + + /// Returns true if the response headers are fully parsed. + bool headers_ready() const { + return (m_state == BODY || m_state == DONE); + } + + /// Returns the full raw response + std::string raw() const; + + /// Set response status code and message + /** + * Sets the response status code to `code` and looks up the corresponding + * message for standard codes. Non-standard codes will be entered as Unknown + * use set_status(status_code::value,std::string) overload to set both + * values explicitly. + * + * @param code Code to set + * @param msg Message to set + */ + void set_status(status_code::value code); + + /// Set response status code and message + /** + * Sets the response status code and message to independent custom values. + * use set_status(status_code::value) to set the code and have the standard + * message be automatically set. + * + * @param code Code to set + * @param msg Message to set + */ + void set_status(status_code::value code, std::string const & msg); + + /// Return the response status code + status_code::value get_status_code() const { + return m_status_code; + } + + /// Return the response status message + const std::string& get_status_msg() const { + return m_status_msg; + } +private: + /// Helper function for consume. Process response line + void process(std::string::iterator begin, std::string::iterator end); + + /// Helper function for processing body bytes + size_t process_body(char const * buf, size_t len); + + enum state { + RESPONSE_LINE = 0, + HEADERS = 1, + BODY = 2, + DONE = 3 + }; + + std::string m_status_msg; + size_t m_read; + lib::shared_ptr m_buf; + status_code::value m_status_code; + state m_state; + +}; + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#include + +#endif // HTTP_PARSER_RESPONSE_HPP diff --git a/subprojects/websocketpp/websocketpp/impl/connection_impl.hpp b/subprojects/websocketpp/websocketpp/impl/connection_impl.hpp new file mode 100644 index 000000000..bf88c9552 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/impl/connection_impl.hpp @@ -0,0 +1,2375 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP +#define WEBSOCKETPP_CONNECTION_IMPL_HPP + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace websocketpp { + +namespace istate = session::internal_state; + +template +void connection::set_termination_handler( + termination_handler new_handler) +{ + m_alog->write(log::alevel::devel, + "connection set_termination_handler"); + + //scoped_lock_type lock(m_connection_state_lock); + + m_termination_handler = new_handler; +} + +template +std::string const & connection::get_origin() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_processor->get_origin(m_request); +} + +template +size_t connection::get_buffered_amount() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_send_buffer_size; +} + +template +session::state::value connection::get_state() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_state; +} + +template +lib::error_code connection::send(std::string const & payload, + frame::opcode::value op) +{ + message_ptr msg = m_msg_manager->get_message(op,payload.size()); + msg->append_payload(payload); + msg->set_compressed(true); + + return send(msg); +} + +template +lib::error_code connection::send(void const * payload, size_t len, + frame::opcode::value op) +{ + message_ptr msg = m_msg_manager->get_message(op,len); + msg->append_payload(payload,len); + + return send(msg); +} + +template +lib::error_code connection::send(typename config::message_type::ptr msg) +{ + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"connection send"); + } + + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + return error::make_error_code(error::invalid_state); + } + } + + message_ptr outgoing_msg; + bool needs_writing = false; + + if (msg->get_prepared()) { + outgoing_msg = msg; + + scoped_lock_type lock(m_write_lock); + write_push(outgoing_msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } else { + outgoing_msg = m_msg_manager->get_message(); + + if (!outgoing_msg) { + return error::make_error_code(error::no_outgoing_buffers); + } + + scoped_lock_type lock(m_write_lock); + lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg); + + if (ec) { + return ec; + } + + write_push(outgoing_msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + return lib::error_code(); +} + +template +void connection::ping(std::string const& payload, lib::error_code& ec) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"connection ping"); + } + + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + std::stringstream ss; + ss << "connection::ping called from invalid state " << m_state; + m_alog->write(log::alevel::devel,ss.str()); + ec = error::make_error_code(error::invalid_state); + return; + } + } + + message_ptr msg = m_msg_manager->get_message(); + if (!msg) { + ec = error::make_error_code(error::no_outgoing_buffers); + return; + } + + ec = m_processor->prepare_ping(payload,msg); + if (ec) {return;} + + // set ping timer if we are listening for one + if (m_pong_timeout_handler) { + // Cancel any existing timers + if (m_ping_timer) { + m_ping_timer->cancel(); + } + + if (m_pong_timeout_dur > 0) { + m_ping_timer = transport_con_type::set_timer( + m_pong_timeout_dur, + lib::bind( + &type::handle_pong_timeout, + type::get_shared(), + payload, + lib::placeholders::_1 + ) + ); + } + + if (!m_ping_timer) { + // Our transport doesn't support timers + m_elog->write(log::elevel::warn,"Warning: a pong_timeout_handler is \ + set but the transport in use does not support timeouts."); + } + } + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + write_push(msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + ec = lib::error_code(); +} + +template +void connection::ping(std::string const & payload) { + lib::error_code ec; + ping(payload,ec); + if (ec) { + throw exception(ec); + } +} + +template +void connection::handle_pong_timeout(std::string payload, + lib::error_code const & ec) +{ + if (ec) { + if (ec == transport::error::operation_aborted) { + // ignore, this is expected + return; + } + + m_elog->write(log::elevel::devel,"pong_timeout error: "+ec.message()); + return; + } + + if (m_pong_timeout_handler) { + m_pong_timeout_handler(m_connection_hdl,payload); + } +} + +template +void connection::pong(std::string const& payload, lib::error_code& ec) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"connection pong"); + } + + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + std::stringstream ss; + ss << "connection::pong called from invalid state " << m_state; + m_alog->write(log::alevel::devel,ss.str()); + ec = error::make_error_code(error::invalid_state); + return; + } + } + + message_ptr msg = m_msg_manager->get_message(); + if (!msg) { + ec = error::make_error_code(error::no_outgoing_buffers); + return; + } + + ec = m_processor->prepare_pong(payload,msg); + if (ec) {return;} + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + write_push(msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + ec = lib::error_code(); +} + +template +void connection::pong(std::string const & payload) { + lib::error_code ec; + pong(payload,ec); + if (ec) { + throw exception(ec); + } +} + +template +void connection::close(close::status::value const code, + std::string const & reason, lib::error_code & ec) +{ + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"connection close"); + } + + // Truncate reason to maximum size allowable in a close frame. + std::string tr(reason,0,std::min(reason.size(), + frame::limits::close_reason_size)); + + scoped_lock_type lock(m_connection_state_lock); + + if (m_state != session::state::open) { + ec = error::make_error_code(error::invalid_state); + return; + } + + ec = this->send_close_frame(code,tr,false,close::status::terminal(code)); +} + +template +void connection::close(close::status::value const code, + std::string const & reason) +{ + lib::error_code ec; + close(code,reason,ec); + if (ec) { + throw exception(ec); + } +} + +/// Trigger the on_interrupt handler +/** + * This is thread safe if the transport is thread safe + */ +template +lib::error_code connection::interrupt() { + m_alog->write(log::alevel::devel,"connection connection::interrupt"); + return transport_con_type::interrupt( + lib::bind( + &type::handle_interrupt, + type::get_shared() + ) + ); +} + + +template +void connection::handle_interrupt() { + if (m_interrupt_handler) { + m_interrupt_handler(m_connection_hdl); + } +} + +template +lib::error_code connection::pause_reading() { + m_alog->write(log::alevel::devel,"connection connection::pause_reading"); + return transport_con_type::dispatch( + lib::bind( + &type::handle_pause_reading, + type::get_shared() + ) + ); +} + +/// Pause reading handler. Not safe to call directly +template +void connection::handle_pause_reading() { + m_alog->write(log::alevel::devel,"connection connection::handle_pause_reading"); + m_read_flag = false; +} + +template +lib::error_code connection::resume_reading() { + m_alog->write(log::alevel::devel,"connection connection::resume_reading"); + return transport_con_type::dispatch( + lib::bind( + &type::handle_resume_reading, + type::get_shared() + ) + ); +} + +/// Resume reading helper method. Not safe to call directly +template +void connection::handle_resume_reading() { + m_read_flag = true; + read_frame(); +} + + + + + + + + + + + +template +bool connection::get_secure() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_secure(); +} + +template +std::string const & connection::get_host() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_host(); +} + +template +std::string const & connection::get_resource() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_resource(); +} + +template +uint16_t connection::get_port() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_port(); +} + +template +uri_ptr connection::get_uri() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri; +} + +template +void connection::set_uri(uri_ptr uri) { + //scoped_lock_type lock(m_connection_state_lock); + m_uri = uri; +} + + + + + + +template +std::string const & connection::get_subprotocol() const { + return m_subprotocol; +} + +template +std::vector const & +connection::get_requested_subprotocols() const { + return m_requested_subprotocols; +} + +template +void connection::add_subprotocol(std::string const & value, + lib::error_code & ec) +{ + if (m_is_server) { + ec = error::make_error_code(error::client_only); + return; + } + + // If the value is empty or has a non-RFC2616 token character it is invalid. + if (value.empty() || std::find_if(value.begin(),value.end(), + http::is_not_token_char) != value.end()) + { + ec = error::make_error_code(error::invalid_subprotocol); + return; + } + + m_requested_subprotocols.push_back(value); +} + +template +void connection::add_subprotocol(std::string const & value) { + lib::error_code ec; + this->add_subprotocol(value,ec); + if (ec) { + throw exception(ec); + } +} + + +template +void connection::select_subprotocol(std::string const & value, + lib::error_code & ec) +{ + if (!m_is_server) { + ec = error::make_error_code(error::server_only); + return; + } + + if (value.empty()) { + ec = lib::error_code(); + return; + } + + std::vector::iterator it; + + it = std::find(m_requested_subprotocols.begin(), + m_requested_subprotocols.end(), + value); + + if (it == m_requested_subprotocols.end()) { + ec = error::make_error_code(error::unrequested_subprotocol); + return; + } + + m_subprotocol = value; +} + +template +void connection::select_subprotocol(std::string const & value) { + lib::error_code ec; + this->select_subprotocol(value,ec); + if (ec) { + throw exception(ec); + } +} + + +template +std::string const & +connection::get_request_header(std::string const & key) const { + return m_request.get_header(key); +} + +template +std::string const & +connection::get_request_body() const { + return m_request.get_body(); +} + +template +std::string const & +connection::get_response_header(std::string const & key) const { + return m_response.get_header(key); +} + +// TODO: EXCEPTION_FREE +template +void connection::set_status(http::status_code::value code) +{ + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); + } + m_response.set_status(code); +} + +// TODO: EXCEPTION_FREE +template +void connection::set_status(http::status_code::value code, + std::string const & msg) +{ + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); + } + + m_response.set_status(code,msg); +} + +// TODO: EXCEPTION_FREE +template +void connection::set_body(std::string const & value) { + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); + } + + m_response.set_body(value); +} + +// TODO: EXCEPTION_FREE +template +void connection::append_header(std::string const & key, + std::string const & val) +{ + if (m_is_server) { + if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { + // we are setting response headers for an incoming server connection + m_response.append_header(key,val); + } else { + throw exception("Call to append_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } else { + if (m_internal_state == istate::USER_INIT) { + // we are setting initial headers for an outgoing client connection + m_request.append_header(key,val); + } else { + throw exception("Call to append_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } +} + +// TODO: EXCEPTION_FREE +template +void connection::replace_header(std::string const & key, + std::string const & val) +{ + if (m_is_server) { + if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { + // we are setting response headers for an incoming server connection + m_response.replace_header(key,val); + } else { + throw exception("Call to replace_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } else { + if (m_internal_state == istate::USER_INIT) { + // we are setting initial headers for an outgoing client connection + m_request.replace_header(key,val); + } else { + throw exception("Call to replace_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } +} + +// TODO: EXCEPTION_FREE +template +void connection::remove_header(std::string const & key) +{ + if (m_is_server) { + if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { + // we are setting response headers for an incoming server connection + m_response.remove_header(key); + } else { + throw exception("Call to remove_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } else { + if (m_internal_state == istate::USER_INIT) { + // we are setting initial headers for an outgoing client connection + m_request.remove_header(key); + } else { + throw exception("Call to remove_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } +} + +/// Defer HTTP Response until later +/** + * Used in the http handler to defer the HTTP response for this connection + * until later. Handshake timers will be canceled and the connection will be + * left open until `send_http_response` or an equivalent is called. + * + * Warning: deferred connections won't time out and as a result can tie up + * resources. + * + * @return A status code, zero on success, non-zero otherwise + */ +template +lib::error_code connection::defer_http_response() { + // Cancel handshake timer, otherwise the connection will time out and we'll + // close the connection before the app has a chance to send a response. + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + // Do something to signal deferral + m_http_state = session::http_state::deferred; + + return lib::error_code(); +} + +/// Send deferred HTTP Response (exception free) +/** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * @since 0.6.0 + * + * @param ec A status code, zero on success, non-zero otherwise + */ +template +void connection::send_http_response(lib::error_code & ec) { + { + scoped_lock_type lock(m_connection_state_lock); + if (m_http_state != session::http_state::deferred) { + ec = error::make_error_code(error::invalid_state); + return; + } + + m_http_state = session::http_state::body_written; + } + + this->write_http_response(lib::error_code()); + ec = lib::error_code(); +} + +template +void connection::send_http_response() { + lib::error_code ec; + this->send_http_response(ec); + if (ec) { + throw exception(ec); + } +} + + + + +/******** logic thread ********/ + +template +void connection::start() { + m_alog->write(log::alevel::devel,"connection start"); + + if (m_internal_state != istate::USER_INIT) { + m_alog->write(log::alevel::devel,"Start called in invalid state"); + this->terminate(error::make_error_code(error::invalid_state)); + return; + } + + m_internal_state = istate::TRANSPORT_INIT; + + // Depending on how the transport implements init this function may return + // immediately and call handle_transport_init later or call + // handle_transport_init from this function. + transport_con_type::init( + lib::bind( + &type::handle_transport_init, + type::get_shared(), + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_transport_init(lib::error_code const & ec) { + m_alog->write(log::alevel::devel,"connection handle_transport_init"); + + lib::error_code ecm = ec; + + if (m_internal_state != istate::TRANSPORT_INIT) { + m_alog->write(log::alevel::devel, + "handle_transport_init must be called from transport init state"); + ecm = error::make_error_code(error::invalid_state); + } + + if (ecm) { + std::stringstream s; + s << "handle_transport_init received error: "<< ecm.message(); + m_elog->write(log::elevel::rerror,s.str()); + + this->terminate(ecm); + return; + } + + // At this point the transport is ready to read and write bytes. + if (m_is_server) { + m_internal_state = istate::READ_HTTP_REQUEST; + this->read_handshake(1); + } else { + // We are a client. Set the processor to the version specified in the + // config file and send a handshake request. + m_internal_state = istate::WRITE_HTTP_REQUEST; + m_processor = get_processor(config::client_version); + this->send_http_request(); + } +} + +template +void connection::read_handshake(size_t num_bytes) { + m_alog->write(log::alevel::devel,"connection read_handshake"); + + if (m_open_handshake_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_open_handshake_timeout_dur, + lib::bind( + &type::handle_open_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + + transport_con_type::async_read_at_least( + num_bytes, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_handshake, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); +} + +// All exit paths for this function need to call write_http_response() or submit +// a new read request with this function as the handler. +template +void connection::handle_read_handshake(lib::error_code const & ec, + size_t bytes_transferred) +{ + m_alog->write(log::alevel::devel,"connection handle_read_handshake"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::READ_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog->write(log::alevel::devel, + "handle_read_handshake invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog->write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_read_handshake",ecm); + this->terminate(ecm); + return; + } + + // Boundaries checking. TODO: How much of this should be done? + if (bytes_transferred > config::connection_read_buffer_size) { + m_elog->write(log::elevel::fatal,"Fatal boundaries checking error."); + this->terminate(make_error_code(error::general)); + return; + } + + size_t bytes_processed = 0; + try { + bytes_processed = m_request.consume(m_buf,bytes_transferred); + } catch (http::exception &e) { + // All HTTP exceptions will result in this request failing and an error + // response being returned. No more bytes will be read in this con. + m_response.set_status(e.m_error_code,e.m_error_msg); + this->write_http_response_error(error::make_error_code(error::http_parse_error)); + return; + } + + // More paranoid boundaries checking. + // TODO: Is this overkill? + if (bytes_processed > bytes_transferred) { + m_elog->write(log::elevel::fatal,"Fatal boundaries checking error."); + this->terminate(make_error_code(error::general)); + return; + } + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "bytes_transferred: " << bytes_transferred + << " bytes, bytes processed: " << bytes_processed << " bytes"; + m_alog->write(log::alevel::devel,s.str()); + } + + if (m_request.ready()) { + lib::error_code processor_ec = this->initialize_processor(); + if (processor_ec) { + this->write_http_response_error(processor_ec); + return; + } + + if (m_processor && m_processor->get_version() == 0) { + // Version 00 has an extra requirement to read some bytes after the + // handshake + if (bytes_transferred-bytes_processed >= 8) { + m_request.replace_header( + "Sec-WebSocket-Key3", + std::string(m_buf+bytes_processed,m_buf+bytes_processed+8) + ); + bytes_processed += 8; + } else { + // TODO: need more bytes + m_alog->write(log::alevel::devel,"short key3 read"); + m_response.set_status(http::status_code::internal_server_error); + this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3)); + return; + } + } + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,m_request.raw()); + if (!m_request.get_header("Sec-WebSocket-Key3").empty()) { + m_alog->write(log::alevel::devel, + utility::to_hex(m_request.get_header("Sec-WebSocket-Key3"))); + } + } + + // The remaining bytes in m_buf are frame data. Copy them to the + // beginning of the buffer and note the length. They will be read after + // the handshake completes and before more bytes are read. + std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf); + m_buf_cursor = bytes_transferred-bytes_processed; + + + m_internal_state = istate::PROCESS_HTTP_REQUEST; + + // We have the complete request. Process it. + lib::error_code handshake_ec = this->process_handshake_request(); + + // Write a response if this is a websocket connection or if it is an + // HTTP connection for which the response has not been deferred or + // started yet by a different system (i.e. still in init state). + if (!m_is_http || m_http_state == session::http_state::init) { + this->write_http_response(handshake_ec); + } + } else { + // read at least 1 more byte + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_handshake, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } +} + +// write_http_response requires the request to be fully read and the connection +// to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors +// before the request is fully read (specifically at a point where we aren't +// sure if the hybi00 key3 bytes need to be read). This method sets the correct +// state and calls write_http_response +template +void connection::write_http_response_error(lib::error_code const & ec) { + if (m_internal_state != istate::READ_HTTP_REQUEST) { + m_alog->write(log::alevel::devel, + "write_http_response_error called in invalid state"); + this->terminate(error::make_error_code(error::invalid_state)); + return; + } + + m_internal_state = istate::PROCESS_HTTP_REQUEST; + + this->write_http_response(ec); +} + +// All exit paths for this function need to call write_http_response() or submit +// a new read request with this function as the handler. +template +void connection::handle_read_frame(lib::error_code const & ec, + size_t bytes_transferred) +{ + //m_alog->write(log::alevel::devel,"connection handle_read_frame"); + + lib::error_code ecm = ec; + + if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) { + ecm = error::make_error_code(error::invalid_state); + } + + if (ecm) { + log::level echannel = log::elevel::rerror; + + if (ecm == transport::error::eof) { + if (m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + // just ignore it + m_alog->write(log::alevel::devel,"got eof from closed con"); + return; + } else if (m_state == session::state::closing && !m_is_server) { + // If we are a client we expect to get eof in the closing state, + // this is a signal to terminate our end of the connection after + // the closing handshake + terminate(lib::error_code()); + return; + } + } else if (ecm == error::invalid_state) { + // In general, invalid state errors in the closed state are the + // result of handlers that were in the system already when the state + // changed and should be ignored as they pose no problems and there + // is nothing useful that we can do about them. + if (m_state == session::state::closed) { + m_alog->write(log::alevel::devel, + "handle_read_frame: got invalid istate in closed state"); + return; + } + } else if (ecm == transport::error::action_after_shutdown) { + echannel = log::elevel::info; + } else { + // TODO: more generally should we do something different here in the + // case that m_state is cosed? Are errors after the connection is + // already closed really an rerror? + } + + + + log_err(echannel, "handle_read_frame", ecm); + this->terminate(ecm); + return; + } + + // Boundaries checking. TODO: How much of this should be done? + /*if (bytes_transferred > config::connection_read_buffer_size) { + m_elog->write(log::elevel::fatal,"Fatal boundaries checking error"); + this->terminate(make_error_code(error::general)); + return; + }*/ + + size_t p = 0; + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "p = " << p << " bytes transferred = " << bytes_transferred; + m_alog->write(log::alevel::devel,s.str()); + } + + while (p < bytes_transferred) { + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "calling consume with " << bytes_transferred-p << " bytes"; + m_alog->write(log::alevel::devel,s.str()); + } + + lib::error_code consume_ec; + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "Processing Bytes: " << utility::to_hex(reinterpret_cast(m_buf)+p,bytes_transferred-p); + m_alog->write(log::alevel::devel,s.str()); + } + + p += m_processor->consume( + reinterpret_cast(m_buf)+p, + bytes_transferred-p, + consume_ec + ); + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "bytes left after consume: " << bytes_transferred-p; + m_alog->write(log::alevel::devel,s.str()); + } + if (consume_ec) { + log_err(log::elevel::rerror, "consume", consume_ec); + + if (config::drop_on_protocol_error) { + this->terminate(consume_ec); + return; + } else { + lib::error_code close_ec; + this->close( + processor::error::to_ws(consume_ec), + consume_ec.message(), + close_ec + ); + + if (close_ec) { + log_err(log::elevel::fatal, "Protocol error close frame ", close_ec); + this->terminate(close_ec); + return; + } + } + return; + } + + if (m_processor->ready()) { + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "Complete message received. Dispatching"; + m_alog->write(log::alevel::devel,s.str()); + } + + message_ptr msg = m_processor->get_message(); + + if (!msg) { + m_alog->write(log::alevel::devel, "null message from m_processor"); + } else if (!is_control(msg->get_opcode())) { + // data message, dispatch to user + if (m_state != session::state::open) { + m_elog->write(log::elevel::warn, "got non-close frame while closing"); + } else if (m_message_handler) { + m_message_handler(m_connection_hdl, msg); + } + } else { + process_control_frame(msg); + } + } + } + + read_frame(); +} + +/// Issue a new transport read unless reading is paused. +template +void connection::read_frame() { + if (!m_read_flag) { + return; + } + + transport_con_type::async_read_at_least( + // std::min wont work with undefined static const values. + // TODO: is there a more elegant way to do this? + // Need to determine if requesting 1 byte or the exact number of bytes + // is better here. 1 byte lets us be a bit more responsive at a + // potential expense of additional runs through handle_read_frame + /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ? + config::connection_read_buffer_size : m_processor->get_bytes_needed())*/ + 1, + m_buf, + config::connection_read_buffer_size, + m_handle_read_frame + ); +} + +template +lib::error_code connection::initialize_processor() { + m_alog->write(log::alevel::devel,"initialize_processor"); + + // if it isn't a websocket handshake nothing to do. + if (!processor::is_websocket_handshake(m_request)) { + return lib::error_code(); + } + + int version = processor::get_websocket_version(m_request); + + if (version < 0) { + m_alog->write(log::alevel::devel, "BAD REQUEST: can't determine version"); + m_response.set_status(http::status_code::bad_request); + return error::make_error_code(error::invalid_version); + } + + m_processor = get_processor(version); + + // if the processor is not null we are done + if (m_processor) { + return lib::error_code(); + } + + // We don't have a processor for this version. Return bad request + // with Sec-WebSocket-Version header filled with values we do accept + m_alog->write(log::alevel::devel, "BAD REQUEST: no processor for version"); + m_response.set_status(http::status_code::bad_request); + + std::stringstream ss; + std::string sep; + std::vector::const_iterator it; + for (it = versions_supported.begin(); it != versions_supported.end(); it++) + { + ss << sep << *it; + sep = ","; + } + + m_response.replace_header("Sec-WebSocket-Version",ss.str()); + return error::make_error_code(error::unsupported_version); +} + +template +lib::error_code connection::process_handshake_request() { + m_alog->write(log::alevel::devel,"process handshake request"); + + if (!processor::is_websocket_handshake(m_request)) { + // this is not a websocket handshake. Process as plain HTTP + m_alog->write(log::alevel::devel,"HTTP REQUEST"); + + // extract URI from request + m_uri = processor::get_uri_from_host( + m_request, + (transport_con_type::is_secure() ? "https" : "http") + ); + + if (!m_uri->get_valid()) { + m_alog->write(log::alevel::devel, "Bad request: failed to parse uri"); + m_response.set_status(http::status_code::bad_request); + return error::make_error_code(error::invalid_uri); + } + + if (m_http_handler) { + m_is_http = true; + m_http_handler(m_connection_hdl); + + if (m_state == session::state::closed) { + return error::make_error_code(error::http_connection_ended); + } + } else { + set_status(http::status_code::upgrade_required); + return error::make_error_code(error::upgrade_required); + } + + return lib::error_code(); + } + + lib::error_code ec = m_processor->validate_handshake(m_request); + + // Validate: make sure all required elements are present. + if (ec){ + // Not a valid handshake request + m_alog->write(log::alevel::devel, "Bad request " + ec.message()); + m_response.set_status(http::status_code::bad_request); + return ec; + } + + // Read extension parameters and set up values necessary for the end user + // to complete extension negotiation. + std::pair neg_results; + neg_results = m_processor->negotiate_extensions(m_request); + + if (neg_results.first == processor::error::make_error_code(processor::error::extension_parse_error)) { + // There was a fatal error in extension parsing that should result in + // a failed connection attempt. + m_elog->write(log::elevel::info, "Bad request: " + neg_results.first.message()); + m_response.set_status(http::status_code::bad_request); + return neg_results.first; + } else if (neg_results.first) { + // There was a fatal error in extension processing that is probably our + // fault. Consider extension negotiation to have failed and continue as + // if extensions were not supported + m_elog->write(log::elevel::info, + "Extension negotiation failed: " + neg_results.first.message()); + } else { + // extension negotiation succeeded, set response header accordingly + // we don't send an empty extensions header because it breaks many + // clients. + if (neg_results.second.size() > 0) { + m_response.replace_header("Sec-WebSocket-Extensions", + neg_results.second); + } + } + + // extract URI from request + m_uri = m_processor->get_uri(m_request); + + + if (!m_uri->get_valid()) { + m_alog->write(log::alevel::devel, "Bad request: failed to parse uri"); + m_response.set_status(http::status_code::bad_request); + return error::make_error_code(error::invalid_uri); + } + + // extract subprotocols + lib::error_code subp_ec = m_processor->extract_subprotocols(m_request, + m_requested_subprotocols); + + if (subp_ec) { + // should we do anything? + } + + // Ask application to validate the connection + if (!m_validate_handler || m_validate_handler(m_connection_hdl)) { + m_response.set_status(http::status_code::switching_protocols); + + // Write the appropriate response headers based on request and + // processor version + ec = m_processor->process_handshake(m_request,m_subprotocol,m_response); + + if (ec) { + std::stringstream s; + s << "Processing error: " << ec << "(" << ec.message() << ")"; + m_alog->write(log::alevel::devel, s.str()); + + m_response.set_status(http::status_code::internal_server_error); + return ec; + } + } else { + // User application has rejected the handshake + m_alog->write(log::alevel::devel, "USER REJECT"); + + // Use Bad Request if the user handler did not provide a more + // specific http response error code. + // TODO: is there a better default? + if (m_response.get_status_code() == http::status_code::uninitialized) { + m_response.set_status(http::status_code::bad_request); + } + + return error::make_error_code(error::rejected); + } + + return lib::error_code(); +} + +template +void connection::write_http_response(lib::error_code const & ec) { + m_alog->write(log::alevel::devel,"connection write_http_response"); + + if (ec == error::make_error_code(error::http_connection_ended)) { + m_alog->write(log::alevel::http,"An HTTP handler took over the connection."); + return; + } + + if (m_response.get_status_code() == http::status_code::uninitialized) { + m_response.set_status(http::status_code::internal_server_error); + m_ec = error::make_error_code(error::general); + } else { + m_ec = ec; + } + + m_response.set_version("HTTP/1.1"); + + // Set server header based on the user agent settings + if (m_response.get_header("Server").empty()) { + if (!m_user_agent.empty()) { + m_response.replace_header("Server",m_user_agent); + } else { + m_response.remove_header("Server"); + } + } + + // have the processor generate the raw bytes for the wire (if it exists) + if (m_processor) { + m_handshake_buffer = m_processor->get_raw(m_response); + } else { + // a processor wont exist for raw HTTP responses. + m_handshake_buffer = m_response.raw(); + } + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer); + if (!m_response.get_header("Sec-WebSocket-Key3").empty()) { + m_alog->write(log::alevel::devel, + utility::to_hex(m_response.get_header("Sec-WebSocket-Key3"))); + } + } + + // write raw bytes + transport_con_type::async_write( + m_handshake_buffer.data(), + m_handshake_buffer.size(), + lib::bind( + &type::handle_write_http_response, + type::get_shared(), + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_write_http_response(lib::error_code const & ec) { + m_alog->write(log::alevel::devel,"handle_write_http_response"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog->write(log::alevel::devel, + "handle_write_http_response invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog->write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_write_http_response",ecm); + this->terminate(ecm); + return; + } + + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + if (m_response.get_status_code() != http::status_code::switching_protocols) + { + /*if (m_processor || m_ec == error::http_parse_error || + m_ec == error::invalid_version || m_ec == error::unsupported_version + || m_ec == error::upgrade_required) + {*/ + if (!m_is_http) { + std::stringstream s; + s << "Handshake ended with HTTP error: " + << m_response.get_status_code(); + m_elog->write(log::elevel::rerror,s.str()); + } else { + // if this was not a websocket connection, we have written + // the expected response and the connection can be closed. + + this->log_http_result(); + + if (m_ec) { + m_alog->write(log::alevel::devel, + "got to writing HTTP results with m_ec set: "+m_ec.message()); + } + m_ec = make_error_code(error::http_connection_ended); + } + + this->terminate(m_ec); + return; + } + + this->log_open_result(); + + m_internal_state = istate::PROCESS_CONNECTION; + m_state = session::state::open; + + if (m_open_handler) { + m_open_handler(m_connection_hdl); + } + + this->handle_read_frame(lib::error_code(), m_buf_cursor); +} + +template +void connection::send_http_request() { + m_alog->write(log::alevel::devel,"connection send_http_request"); + + // TODO: origin header? + + // Have the protocol processor fill in the appropriate fields based on the + // selected client version + if (m_processor) { + lib::error_code ec; + ec = m_processor->client_handshake_request(m_request,m_uri, + m_requested_subprotocols); + + if (ec) { + log_err(log::elevel::fatal,"Internal library error: Processor",ec); + return; + } + } else { + m_elog->write(log::elevel::fatal,"Internal library error: missing processor"); + return; + } + + // Unless the user has overridden the user agent, send generic WS++ UA. + if (m_request.get_header("User-Agent").empty()) { + if (!m_user_agent.empty()) { + m_request.replace_header("User-Agent",m_user_agent); + } else { + m_request.remove_header("User-Agent"); + } + } + + m_handshake_buffer = m_request.raw(); + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer); + } + + if (m_open_handshake_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_open_handshake_timeout_dur, + lib::bind( + &type::handle_open_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + + transport_con_type::async_write( + m_handshake_buffer.data(), + m_handshake_buffer.size(), + lib::bind( + &type::handle_send_http_request, + type::get_shared(), + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_send_http_request(lib::error_code const & ec) { + m_alog->write(log::alevel::devel,"handle_send_http_request"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::WRITE_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } else { + m_internal_state = istate::READ_HTTP_RESPONSE; + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog->write(log::alevel::devel, + "handle_send_http_request invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog->write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_send_http_request",ecm); + this->terminate(ecm); + return; + } + + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_http_response, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); +} + +template +void connection::handle_read_http_response(lib::error_code const & ec, + size_t bytes_transferred) +{ + m_alog->write(log::alevel::devel,"handle_read_http_response"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::READ_HTTP_RESPONSE) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog->write(log::alevel::devel, + "handle_read_http_response invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog->write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_read_http_response",ecm); + this->terminate(ecm); + return; + } + + size_t bytes_processed = 0; + // TODO: refactor this to use error codes rather than exceptions + try { + bytes_processed = m_response.consume(m_buf,bytes_transferred); + } catch (http::exception & e) { + m_elog->write(log::elevel::rerror, + std::string("error in handle_read_http_response: ")+e.what()); + this->terminate(make_error_code(error::general)); + return; + } + + m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); + + if (m_response.headers_ready()) { + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + lib::error_code validate_ec = m_processor->validate_server_handshake_response( + m_request, + m_response + ); + if (validate_ec) { + log_err(log::elevel::rerror,"Server handshake response",validate_ec); + this->terminate(validate_ec); + return; + } + + // Read extension parameters and set up values necessary for the end + // user to complete extension negotiation. + std::pair neg_results; + neg_results = m_processor->negotiate_extensions(m_response); + + if (neg_results.first) { + // There was a fatal error in extension negotiation. For the moment + // kill all connections that fail extension negotiation. + + // TODO: deal with cases where the response is well formed but + // doesn't match the options requested by the client. Its possible + // that the best behavior in this cases is to log and continue with + // an unextended connection. + m_alog->write(log::alevel::devel, "Extension negotiation failed: " + + neg_results.first.message()); + this->terminate(make_error_code(error::extension_neg_failed)); + // TODO: close connection with reason 1010 (and list extensions) + } + + // response is valid, connection can now be assumed to be open + m_internal_state = istate::PROCESS_CONNECTION; + m_state = session::state::open; + + this->log_open_result(); + + if (m_open_handler) { + m_open_handler(m_connection_hdl); + } + + // The remaining bytes in m_buf are frame data. Copy them to the + // beginning of the buffer and note the length. They will be read after + // the handshake completes and before more bytes are read. + std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf); + m_buf_cursor = bytes_transferred-bytes_processed; + + this->handle_read_frame(lib::error_code(), m_buf_cursor); + } else { + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_http_response, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } +} + +template +void connection::handle_open_handshake_timeout( + lib::error_code const & ec) +{ + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel,"open handshake timer cancelled"); + } else if (ec) { + m_alog->write(log::alevel::devel, + "open handle_open_handshake_timeout error: "+ec.message()); + // TODO: ignore or fail here? + } else { + m_alog->write(log::alevel::devel,"open handshake timer expired"); + terminate(make_error_code(error::open_handshake_timeout)); + } +} + +template +void connection::handle_close_handshake_timeout( + lib::error_code const & ec) +{ + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel,"asio close handshake timer cancelled"); + } else if (ec) { + m_alog->write(log::alevel::devel, + "asio open handle_close_handshake_timeout error: "+ec.message()); + // TODO: ignore or fail here? + } else { + m_alog->write(log::alevel::devel, "asio close handshake timer expired"); + terminate(make_error_code(error::close_handshake_timeout)); + } +} + +template +void connection::terminate(lib::error_code const & ec) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"connection terminate"); + } + + // Cancel close handshake timer + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + terminate_status tstat = unknown; + if (ec) { + m_ec = ec; + m_local_close_code = close::status::abnormal_close; + m_local_close_reason = ec.message(); + } + + // TODO: does any of this need a mutex? + if (m_is_http) { + m_http_state = session::http_state::closed; + } + if (m_state == session::state::connecting) { + m_state = session::state::closed; + tstat = failed; + + // Log fail result here before socket is shut down and we can't get + // the remote address, etc anymore + if (m_ec != error::http_connection_ended) { + log_fail_result(); + } + } else if (m_state != session::state::closed) { + m_state = session::state::closed; + tstat = closed; + } else { + m_alog->write(log::alevel::devel, + "terminate called on connection that was already terminated"); + return; + } + + // TODO: choose between shutdown and close based on error code sent + + transport_con_type::async_shutdown( + lib::bind( + &type::handle_terminate, + type::get_shared(), + tstat, + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_terminate(terminate_status tstat, + lib::error_code const & ec) +{ + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"connection handle_terminate"); + } + + if (ec) { + // there was an error actually shutting down the connection + log_err(log::elevel::devel,"handle_terminate",ec); + } + + // clean shutdown + if (tstat == failed) { + if (m_ec != error::http_connection_ended) { + if (m_fail_handler) { + m_fail_handler(m_connection_hdl); + } + } + } else if (tstat == closed) { + if (m_close_handler) { + m_close_handler(m_connection_hdl); + } + log_close_result(); + } else { + m_elog->write(log::elevel::rerror,"Unknown terminate_status"); + } + + // call the termination handler if it exists + // if it exists it might (but shouldn't) refer to a bad memory location. + // If it does, we don't care and should catch and ignore it. + if (m_termination_handler) { + try { + m_termination_handler(type::get_shared()); + } catch (std::exception const & e) { + m_elog->write(log::elevel::warn, + std::string("termination_handler call failed. Reason was: ")+e.what()); + } + } +} + +template +void connection::write_frame() { + //m_alog->write(log::alevel::devel,"connection write_frame"); + + { + scoped_lock_type lock(m_write_lock); + + // Check the write flag. If true, there is an outstanding transport + // write already. In this case we just return. The write handler will + // start a new write if the write queue isn't empty. If false, we set + // the write flag and proceed to initiate a transport write. + if (m_write_flag) { + return; + } + + // pull off all the messages that are ready to write. + // stop if we get a message marked terminal + message_ptr next_message = write_pop(); + while (next_message) { + m_current_msgs.push_back(next_message); + if (!next_message->get_terminal()) { + next_message = write_pop(); + } else { + next_message = message_ptr(); + } + } + + if (m_current_msgs.empty()) { + // there was nothing to send + return; + } else { + // At this point we own the next messages to be sent and are + // responsible for holding the write flag until they are + // successfully sent or there is some error + m_write_flag = true; + } + } + + typename std::vector::iterator it; + for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) { + std::string const & header = (*it)->get_header(); + std::string const & payload = (*it)->get_payload(); + + m_send_buffer.push_back(transport::buffer(header.c_str(),header.size())); + m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size())); + } + + // Print detailed send stats if those log levels are enabled + if (m_alog->static_test(log::alevel::frame_header)) { + if (m_alog->dynamic_test(log::alevel::frame_header)) { + std::stringstream general,header,payload; + + general << "Dispatching write containing " << m_current_msgs.size() + <<" message(s) containing "; + header << "Header Bytes: \n"; + payload << "Payload Bytes: \n"; + + size_t hbytes = 0; + size_t pbytes = 0; + + for (size_t i = 0; i < m_current_msgs.size(); i++) { + hbytes += m_current_msgs[i]->get_header().size(); + pbytes += m_current_msgs[i]->get_payload().size(); + + + header << "[" << i << "] (" + << m_current_msgs[i]->get_header().size() << ") " + << utility::to_hex(m_current_msgs[i]->get_header()) << "\n"; + + if (m_alog->static_test(log::alevel::frame_payload)) { + if (m_alog->dynamic_test(log::alevel::frame_payload)) { + payload << "[" << i << "] (" + << m_current_msgs[i]->get_payload().size() << ") ["<get_opcode()<<"] " + << (m_current_msgs[i]->get_opcode() == frame::opcode::text ? + m_current_msgs[i]->get_payload() : + utility::to_hex(m_current_msgs[i]->get_payload()) + ) + << "\n"; + } + } + } + + general << hbytes << " header bytes and " << pbytes << " payload bytes"; + + m_alog->write(log::alevel::frame_header,general.str()); + m_alog->write(log::alevel::frame_header,header.str()); + m_alog->write(log::alevel::frame_payload,payload.str()); + } + } + + transport_con_type::async_write( + m_send_buffer, + m_write_frame_handler + ); +} + +template +void connection::handle_write_frame(lib::error_code const & ec) +{ + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"connection handle_write_frame"); + } + + bool terminal = m_current_msgs.back()->get_terminal(); + + m_send_buffer.clear(); + m_current_msgs.clear(); + // TODO: recycle instead of deleting + + if (ec) { + log_err(log::elevel::fatal,"handle_write_frame",ec); + this->terminate(ec); + return; + } + + if (terminal) { + this->terminate(lib::error_code()); + return; + } + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + + // release write flag + m_write_flag = false; + + needs_writing = !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } +} + +template +std::vector const & connection::get_supported_versions() const +{ + return versions_supported; +} + +template +void connection::process_control_frame(typename config::message_type::ptr msg) +{ + m_alog->write(log::alevel::devel,"process_control_frame"); + + frame::opcode::value op = msg->get_opcode(); + lib::error_code ec; + + std::stringstream s; + s << "Control frame received with opcode " << op; + m_alog->write(log::alevel::control,s.str()); + + if (m_state == session::state::closed) { + m_elog->write(log::elevel::warn,"got frame in state closed"); + return; + } + if (op != frame::opcode::CLOSE && m_state != session::state::open) { + m_elog->write(log::elevel::warn,"got non-close frame in state closing"); + return; + } + + if (op == frame::opcode::PING) { + bool should_reply = true; + + if (m_ping_handler) { + should_reply = m_ping_handler(m_connection_hdl, msg->get_payload()); + } + + if (should_reply) { + this->pong(msg->get_payload(),ec); + if (ec) { + log_err(log::elevel::devel,"Failed to send response pong",ec); + } + } + } else if (op == frame::opcode::PONG) { + if (m_pong_handler) { + m_pong_handler(m_connection_hdl, msg->get_payload()); + } + if (m_ping_timer) { + m_ping_timer->cancel(); + } + } else if (op == frame::opcode::CLOSE) { + m_alog->write(log::alevel::devel,"got close frame"); + // record close code and reason somewhere + + m_remote_close_code = close::extract_code(msg->get_payload(),ec); + if (ec) { + s.str(""); + if (config::drop_on_protocol_error) { + s << "Received invalid close code " << m_remote_close_code + << " dropping connection per config."; + m_elog->write(log::elevel::devel,s.str()); + this->terminate(ec); + } else { + s << "Received invalid close code " << m_remote_close_code + << " sending acknowledgement and closing"; + m_elog->write(log::elevel::devel,s.str()); + ec = send_close_ack(close::status::protocol_error, + "Invalid close code"); + if (ec) { + log_err(log::elevel::devel,"send_close_ack",ec); + } + } + return; + } + + m_remote_close_reason = close::extract_reason(msg->get_payload(),ec); + if (ec) { + if (config::drop_on_protocol_error) { + m_elog->write(log::elevel::devel, + "Received invalid close reason. Dropping connection per config"); + this->terminate(ec); + } else { + m_elog->write(log::elevel::devel, + "Received invalid close reason. Sending acknowledgement and closing"); + ec = send_close_ack(close::status::protocol_error, + "Invalid close reason"); + if (ec) { + log_err(log::elevel::devel,"send_close_ack",ec); + } + } + return; + } + + if (m_state == session::state::open) { + s.str(""); + s << "Received close frame with code " << m_remote_close_code + << " and reason " << m_remote_close_reason; + m_alog->write(log::alevel::devel,s.str()); + + ec = send_close_ack(); + if (ec) { + log_err(log::elevel::devel,"send_close_ack",ec); + } + } else if (m_state == session::state::closing && !m_was_clean) { + // ack of our close + m_alog->write(log::alevel::devel, "Got acknowledgement of close"); + + m_was_clean = true; + + // If we are a server terminate the connection now. Clients should + // leave the connection open to give the server an opportunity to + // initiate the TCP close. The client's timer will handle closing + // its side of the connection if the server misbehaves. + // + // TODO: different behavior if the underlying transport doesn't + // support timers? + if (m_is_server) { + terminate(lib::error_code()); + } + } else { + // spurious, ignore + m_elog->write(log::elevel::devel, "Got close frame in wrong state"); + } + } else { + // got an invalid control opcode + m_elog->write(log::elevel::devel, "Got control frame with invalid opcode"); + // initiate protocol error shutdown + } +} + +template +lib::error_code connection::send_close_ack(close::status::value code, + std::string const & reason) +{ + return send_close_frame(code,reason,true,m_is_server); +} + +template +lib::error_code connection::send_close_frame(close::status::value code, + std::string const & reason, bool ack, bool terminal) +{ + m_alog->write(log::alevel::devel,"send_close_frame"); + + // check for special codes + + // If silent close is set, respect it and blank out close information + // Otherwise use whatever has been specified in the parameters. If + // parameters specifies close::status::blank then determine what to do + // based on whether or not this is an ack. If it is not an ack just + // send blank info. If it is an ack then echo the close information from + // the remote endpoint. + if (config::silent_close) { + m_alog->write(log::alevel::devel,"closing silently"); + m_local_close_code = close::status::no_status; + m_local_close_reason.clear(); + } else if (code != close::status::blank) { + m_alog->write(log::alevel::devel,"closing with specified codes"); + m_local_close_code = code; + m_local_close_reason = reason; + } else if (!ack) { + m_alog->write(log::alevel::devel,"closing with no status code"); + m_local_close_code = close::status::no_status; + m_local_close_reason.clear(); + } else if (m_remote_close_code == close::status::no_status) { + m_alog->write(log::alevel::devel, + "acknowledging a no-status close with normal code"); + m_local_close_code = close::status::normal; + m_local_close_reason.clear(); + } else { + m_alog->write(log::alevel::devel,"acknowledging with remote codes"); + m_local_close_code = m_remote_close_code; + m_local_close_reason = m_remote_close_reason; + } + + std::stringstream s; + s << "Closing with code: " << m_local_close_code << ", and reason: " + << m_local_close_reason; + m_alog->write(log::alevel::devel,s.str()); + + message_ptr msg = m_msg_manager->get_message(); + if (!msg) { + return error::make_error_code(error::no_outgoing_buffers); + } + + lib::error_code ec = m_processor->prepare_close(m_local_close_code, + m_local_close_reason,msg); + if (ec) { + return ec; + } + + // Messages flagged terminal will result in the TCP connection being dropped + // after the message has been written. This is typically used when servers + // send an ack and when any endpoint encounters a protocol error + if (terminal) { + msg->set_terminal(true); + } + + m_state = session::state::closing; + + if (ack) { + m_was_clean = true; + } + + // Start a timer so we don't wait forever for the acknowledgement close + // frame + if (m_close_handshake_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_close_handshake_timeout_dur, + lib::bind( + &type::handle_close_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + write_push(msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + return lib::error_code(); +} + +template +typename connection::processor_ptr +connection::get_processor(int version) const { + // TODO: allow disabling certain versions + + processor_ptr p; + + switch (version) { + case 0: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager + ); + break; + case 7: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) + ); + break; + case 8: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) + ); + break; + case 13: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) + ); + break; + default: + return p; + } + + // Settings not configured by the constructor + p->set_max_message_size(m_max_message_size); + + return p; +} + +template +void connection::write_push(typename config::message_type::ptr msg) +{ + if (!msg) { + return; + } + + m_send_buffer_size += msg->get_payload().size(); + m_send_queue.push(msg); + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "write_push: message count: " << m_send_queue.size() + << " buffer size: " << m_send_buffer_size; + m_alog->write(log::alevel::devel,s.str()); + } +} + +template +typename config::message_type::ptr connection::write_pop() +{ + message_ptr msg; + + if (m_send_queue.empty()) { + return msg; + } + + msg = m_send_queue.front(); + + m_send_buffer_size -= msg->get_payload().size(); + m_send_queue.pop(); + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "write_pop: message count: " << m_send_queue.size() + << " buffer size: " << m_send_buffer_size; + m_alog->write(log::alevel::devel,s.str()); + } + return msg; +} + +template +void connection::log_open_result() +{ + std::stringstream s; + + int version; + if (!processor::is_websocket_handshake(m_request)) { + version = -1; + } else { + version = processor::get_websocket_version(m_request); + } + + // Connection Type + s << (version == -1 ? "HTTP" : "WebSocket") << " Connection "; + + // Remote endpoint address + s << transport_con_type::get_remote_endpoint() << " "; + + // Version string if WebSocket + if (version != -1) { + s << "v" << version << " "; + } + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << "\"\" "; + } else { + // check if there are any quotes in the user agent + s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + // URI + s << (m_uri ? m_uri->get_resource() : "NULL") << " "; + + // Status code + s << m_response.get_status_code(); + + m_alog->write(log::alevel::connect,s.str()); +} + +template +void connection::log_close_result() +{ + std::stringstream s; + + s << "Disconnect " + << "close local:[" << m_local_close_code + << (m_local_close_reason.empty() ? "" : ","+m_local_close_reason) + << "] remote:[" << m_remote_close_code + << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]"; + + m_alog->write(log::alevel::disconnect,s.str()); +} + +template +void connection::log_fail_result() +{ + std::stringstream s; + + int version = processor::get_websocket_version(m_request); + + // Connection Type + s << "WebSocket Connection "; + + // Remote endpoint address & WebSocket version + s << transport_con_type::get_remote_endpoint(); + if (version < 0) { + s << " -"; + } else { + s << " v" << version; + } + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << " \"\" "; + } else { + // check if there are any quotes in the user agent + s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + // URI + s << (m_uri ? m_uri->get_resource() : "-"); + + // HTTP Status code + s << " " << m_response.get_status_code(); + + // WebSocket++ error code & reason + s << " " << m_ec << " " << m_ec.message(); + + m_alog->write(log::alevel::fail,s.str()); +} + +template +void connection::log_http_result() { + std::stringstream s; + + if (processor::is_websocket_handshake(m_request)) { + m_alog->write(log::alevel::devel,"Call to log_http_result for WebSocket"); + return; + } + + // Connection Type + s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host")) + << " " << transport_con_type::get_remote_endpoint() + << " \"" << m_request.get_method() + << " " << (m_uri ? m_uri->get_resource() : "-") + << " " << m_request.get_version() << "\" " << m_response.get_status_code() + << " " << m_response.get_body().size(); + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << " \"\" "; + } else { + // check if there are any quotes in the user agent + s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + m_alog->write(log::alevel::http,s.str()); +} + +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONNECTION_IMPL_HPP diff --git a/subprojects/websocketpp/websocketpp/impl/endpoint_impl.hpp b/subprojects/websocketpp/websocketpp/impl/endpoint_impl.hpp new file mode 100644 index 000000000..2aac1d9da --- /dev/null +++ b/subprojects/websocketpp/websocketpp/impl/endpoint_impl.hpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ENDPOINT_IMPL_HPP +#define WEBSOCKETPP_ENDPOINT_IMPL_HPP + +#include + +namespace websocketpp { + +template +typename endpoint::connection_ptr +endpoint::create_connection() { + m_alog->write(log::alevel::devel,"create_connection"); + //scoped_lock_type lock(m_state_lock); + + /*if (m_state == STOPPING || m_state == STOPPED) { + return connection_ptr(); + }*/ + + //scoped_lock_type guard(m_mutex); + // Create a connection on the heap and manage it using a shared pointer + connection_ptr con = lib::make_shared(m_is_server, + m_user_agent, m_alog, m_elog, lib::ref(m_rng)); + + connection_weak_ptr w(con); + + // Create a weak pointer on the heap using that shared_ptr. + // Cast that weak pointer to void* and manage it using another shared_ptr + // connection_hdl hdl(reinterpret_cast(new connection_weak_ptr(con))); + + con->set_handle(w); + + // Copy default handlers from the endpoint + con->set_open_handler(m_open_handler); + con->set_close_handler(m_close_handler); + con->set_fail_handler(m_fail_handler); + con->set_ping_handler(m_ping_handler); + con->set_pong_handler(m_pong_handler); + con->set_pong_timeout_handler(m_pong_timeout_handler); + con->set_interrupt_handler(m_interrupt_handler); + con->set_http_handler(m_http_handler); + con->set_validate_handler(m_validate_handler); + con->set_message_handler(m_message_handler); + + if (m_open_handshake_timeout_dur != config::timeout_open_handshake) { + con->set_open_handshake_timeout(m_open_handshake_timeout_dur); + } + if (m_close_handshake_timeout_dur != config::timeout_close_handshake) { + con->set_close_handshake_timeout(m_close_handshake_timeout_dur); + } + if (m_pong_timeout_dur != config::timeout_pong) { + con->set_pong_timeout(m_pong_timeout_dur); + } + if (m_max_message_size != config::max_message_size) { + con->set_max_message_size(m_max_message_size); + } + con->set_max_http_body_size(m_max_http_body_size); + + lib::error_code ec; + + ec = transport_type::init(con); + if (ec) { + m_elog->write(log::elevel::fatal,ec.message()); + return connection_ptr(); + } + + return con; +} + +template +void endpoint::interrupt(connection_hdl hdl, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + m_alog->write(log::alevel::devel,"Interrupting connection"); + + ec = con->interrupt(); +} + +template +void endpoint::interrupt(connection_hdl hdl) { + lib::error_code ec; + interrupt(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::pause_reading(connection_hdl hdl, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + ec = con->pause_reading(); +} + +template +void endpoint::pause_reading(connection_hdl hdl) { + lib::error_code ec; + pause_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::resume_reading(connection_hdl hdl, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + ec = con->resume_reading(); +} + +template +void endpoint::resume_reading(connection_hdl hdl) { + lib::error_code ec; + resume_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send_http_response(connection_hdl hdl, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->send_http_response(ec); +} + +template +void endpoint::send_http_response(connection_hdl hdl) { + lib::error_code ec; + send_http_response(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + ec = con->send(payload,op); +} + +template +void endpoint::send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op) +{ + lib::error_code ec; + send(hdl,payload,op,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send(connection_hdl hdl, void const * payload, + size_t len, frame::opcode::value op, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + ec = con->send(payload,len,op); +} + +template +void endpoint::send(connection_hdl hdl, void const * payload, + size_t len, frame::opcode::value op) +{ + lib::error_code ec; + send(hdl,payload,len,op,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send(connection_hdl hdl, message_ptr msg, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + ec = con->send(msg); +} + +template +void endpoint::send(connection_hdl hdl, message_ptr msg) { + lib::error_code ec; + send(hdl,msg,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::close(connection_hdl hdl, close::status::value + const code, std::string const & reason, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->close(code,reason,ec); +} + +template +void endpoint::close(connection_hdl hdl, close::status::value + const code, std::string const & reason) +{ + lib::error_code ec; + close(hdl,code,reason,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::ping(connection_hdl hdl, std::string const & + payload, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->ping(payload,ec); +} + +template +void endpoint::ping(connection_hdl hdl, std::string const & payload) +{ + lib::error_code ec; + ping(hdl,payload,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::pong(connection_hdl hdl, std::string const & payload, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->pong(payload,ec); +} + +template +void endpoint::pong(connection_hdl hdl, std::string const & payload) +{ + lib::error_code ec; + pong(hdl,payload,ec); + if (ec) { throw exception(ec); } +} + +} // namespace websocketpp + +#endif // WEBSOCKETPP_ENDPOINT_IMPL_HPP diff --git a/subprojects/websocketpp/websocketpp/impl/utilities_impl.hpp b/subprojects/websocketpp/websocketpp/impl/utilities_impl.hpp new file mode 100644 index 000000000..6f86e22f5 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/impl/utilities_impl.hpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_UTILITIES_IMPL_HPP +#define WEBSOCKETPP_UTILITIES_IMPL_HPP + +#include +#include + +namespace websocketpp { +namespace utility { + +inline std::string to_lower(std::string const & in) { + std::string out = in; + std::transform(out.begin(),out.end(),out.begin(),::tolower); + return out; +} + +inline std::string to_hex(std::string const & input) { + std::string output; + std::string hex = "0123456789ABCDEF"; + + for (size_t i = 0; i < input.size(); i++) { + output += hex[(input[i] & 0xF0) >> 4]; + output += hex[input[i] & 0x0F]; + output += " "; + } + + return output; +} + +inline std::string to_hex(uint8_t const * input, size_t length) { + std::string output; + std::string hex = "0123456789ABCDEF"; + + for (size_t i = 0; i < length; i++) { + output += hex[(input[i] & 0xF0) >> 4]; + output += hex[input[i] & 0x0F]; + output += " "; + } + + return output; +} + +inline std::string to_hex(const char* input,size_t length) { + return to_hex(reinterpret_cast(input),length); +} + +inline std::string string_replace_all(std::string subject, std::string const & + search, std::string const & replace) +{ + size_t pos = 0; + while((pos = subject.find(search, pos)) != std::string::npos) { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } + return subject; +} + +} // namespace utility +} // namespace websocketpp + +#endif // WEBSOCKETPP_UTILITIES_IMPL_HPP diff --git a/subprojects/websocketpp/websocketpp/logger/basic.hpp b/subprojects/websocketpp/websocketpp/logger/basic.hpp new file mode 100644 index 000000000..84514130e --- /dev/null +++ b/subprojects/websocketpp/websocketpp/logger/basic.hpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_LOGGER_BASIC_HPP +#define WEBSOCKETPP_LOGGER_BASIC_HPP + +/* Need a way to print a message to the log + * + * - timestamps + * - channels + * - thread safe + * - output to stdout or file + * - selective output channels, both compile time and runtime + * - named channels + * - ability to test whether a log message will be printed at compile time + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { +namespace log { + +/// Basic logger that outputs to an ostream +template +class basic { +public: + basic(channel_type_hint::value h = + channel_type_hint::access) + : m_static_channels(0xffffffff) + , m_dynamic_channels(0) + , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {} + + basic(std::ostream * out) + : m_static_channels(0xffffffff) + , m_dynamic_channels(0) + , m_out(out) {} + + basic(level c, channel_type_hint::value h = + channel_type_hint::access) + : m_static_channels(c) + , m_dynamic_channels(0) + , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {} + + basic(level c, std::ostream * out) + : m_static_channels(c) + , m_dynamic_channels(0) + , m_out(out) {} + + /// Destructor + ~basic() {} + + /// Copy constructor + basic(basic const & other) + : m_static_channels(other.m_static_channels) + , m_dynamic_channels(other.m_dynamic_channels) + , m_out(other.m_out) + {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy assignment operator because of const member variables + basic & operator=(basic const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + basic(basic && other) + : m_static_channels(other.m_static_channels) + , m_dynamic_channels(other.m_dynamic_channels) + , m_out(other.m_out) + {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + basic & operator=(basic &&) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + void set_ostream(std::ostream * out = &std::cout) { + m_out = out; + } + + void set_channels(level channels) { + if (channels == names::none) { + clear_channels(names::all); + return; + } + + scoped_lock_type lock(m_lock); + m_dynamic_channels |= (channels & m_static_channels); + } + + void clear_channels(level channels) { + scoped_lock_type lock(m_lock); + m_dynamic_channels &= ~channels; + } + + /// Write a string message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, std::string const & msg) { + scoped_lock_type lock(m_lock); + if (!this->dynamic_test(channel)) { return; } + *m_out << "[" << timestamp << "] " + << "[" << names::channel_name(channel) << "] " + << msg << "\n"; + m_out->flush(); + } + + /// Write a cstring message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, char const * msg) { + scoped_lock_type lock(m_lock); + if (!this->dynamic_test(channel)) { return; } + *m_out << "[" << timestamp << "] " + << "[" << names::channel_name(channel) << "] " + << msg << "\n"; + m_out->flush(); + } + + _WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level channel) const { + return ((channel & m_static_channels) != 0); + } + + bool dynamic_test(level channel) { + return ((channel & m_dynamic_channels) != 0); + } + +protected: + typedef typename concurrency::scoped_lock_type scoped_lock_type; + typedef typename concurrency::mutex_type mutex_type; + mutex_type m_lock; + +private: + // The timestamp does not include the time zone, because on Windows with the + // default registry settings, the time zone would be written out in full, + // which would be obnoxiously verbose. + // + // TODO: find a workaround for this or make this format user settable + static std::ostream & timestamp(std::ostream & os) { + std::time_t t = std::time(NULL); + std::tm lt = lib::localtime(t); + #ifdef _WEBSOCKETPP_PUTTIME_ + return os << std::put_time(<,"%Y-%m-%d %H:%M:%S"); + #else // Falls back to strftime, which requires a temporary copy of the string. + char buffer[20]; + size_t result = std::strftime(buffer,sizeof(buffer),"%Y-%m-%d %H:%M:%S",<); + return os << (result == 0 ? "Unknown" : buffer); + #endif + } + + level const m_static_channels; + level m_dynamic_channels; + std::ostream * m_out; +}; + +} // log +} // websocketpp + +#endif // WEBSOCKETPP_LOGGER_BASIC_HPP diff --git a/subprojects/websocketpp/websocketpp/logger/levels.hpp b/subprojects/websocketpp/websocketpp/logger/levels.hpp new file mode 100644 index 000000000..cd7ccd690 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/logger/levels.hpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_LOGGER_LEVELS_HPP +#define WEBSOCKETPP_LOGGER_LEVELS_HPP + +#include + +namespace websocketpp { +namespace log { + +/// Type of a channel package +typedef uint32_t level; + +/// Package of values for hinting at the nature of a given logger. +/** + * Used by the library to signal to the logging class a hint that it can use to + * set itself up. For example, the `access` hint indicates that it is an access + * log that might be suitable for being printed to an access log file or to cout + * whereas `error` might be suitable for an error log file or cerr. + */ +struct channel_type_hint { + /// Type of a channel type hint value + typedef uint32_t value; + + /// No information + static value const none = 0; + /// Access log + static value const access = 1; + /// Error log + static value const error = 2; +}; + +/// Package of log levels for logging errors +struct elevel { + /// Special aggregate value representing "no levels" + static level const none = 0x0; + /// Low level debugging information (warning: very chatty) + static level const devel = 0x1; + /// Information about unusual system states or other minor internal library + /// problems, less chatty than devel. + static level const library = 0x2; + /// Information about minor configuration problems or additional information + /// about other warnings. + static level const info = 0x4; + /// Information about important problems not severe enough to terminate + /// connections. + static level const warn = 0x8; + /// Recoverable error. Recovery may mean cleanly closing the connection with + /// an appropriate error code to the remote endpoint. + static level const rerror = 0x10; + /// Unrecoverable error. This error will trigger immediate unclean + /// termination of the connection or endpoint. + static level const fatal = 0x20; + /// Special aggregate value representing "all levels" + static level const all = 0xffffffff; + + /// Get the textual name of a channel given a channel id + /** + * The id must be that of a single channel. Passing an aggregate channel + * package results in undefined behavior. + * + * @param channel The channel id to look up. + * + * @return The name of the specified channel. + */ + static char const * channel_name(level channel) { + switch(channel) { + case devel: + return "devel"; + case library: + return "library"; + case info: + return "info"; + case warn: + return "warning"; + case rerror: + return "error"; + case fatal: + return "fatal"; + default: + return "unknown"; + } + } +}; + +/// Package of log levels for logging access events +struct alevel { + /// Special aggregate value representing "no levels" + static level const none = 0x0; + /// Information about new connections + /** + * One line for each new connection that includes a host of information + * including: the remote address, websocket version, requested resource, + * http code, remote user agent + */ + static level const connect = 0x1; + /// One line for each closed connection. Includes closing codes and reasons. + static level const disconnect = 0x2; + /// One line per control frame + static level const control = 0x4; + /// One line per frame, includes the full frame header + static level const frame_header = 0x8; + /// One line per frame, includes the full message payload (warning: chatty) + static level const frame_payload = 0x10; + /// Reserved + static level const message_header = 0x20; + /// Reserved + static level const message_payload = 0x40; + /// Reserved + static level const endpoint = 0x80; + /// Extra information about opening handshakes + static level const debug_handshake = 0x100; + /// Extra information about closing handshakes + static level const debug_close = 0x200; + /// Development messages (warning: very chatty) + static level const devel = 0x400; + /// Special channel for application specific logs. Not used by the library. + static level const app = 0x800; + /// Access related to HTTP requests + static level const http = 0x1000; + /// One line for each failed WebSocket connection with details + static level const fail = 0x2000; + /// Aggregate package representing the commonly used core access channels + /// Connect, Disconnect, Fail, and HTTP + static level const access_core = 0x00003003; + /// Special aggregate value representing "all levels" + static level const all = 0xffffffff; + + /// Get the textual name of a channel given a channel id + /** + * Get the textual name of a channel given a channel id. The id must be that + * of a single channel. Passing an aggregate channel package results in + * undefined behavior. + * + * @param channel The channelid to look up. + * + * @return The name of the specified channel. + */ + static char const * channel_name(level channel) { + switch(channel) { + case connect: + return "connect"; + case disconnect: + return "disconnect"; + case control: + return "control"; + case frame_header: + return "frame_header"; + case frame_payload: + return "frame_payload"; + case message_header: + return "message_header"; + case message_payload: + return "message_payload"; + case endpoint: + return "endpoint"; + case debug_handshake: + return "debug_handshake"; + case debug_close: + return "debug_close"; + case devel: + return "devel"; + case app: + return "application"; + case http: + return "http"; + case fail: + return "fail"; + default: + return "unknown"; + } + } +}; + +} // logger +} // websocketpp + +#endif //WEBSOCKETPP_LOGGER_LEVELS_HPP diff --git a/subprojects/websocketpp/websocketpp/logger/stub.hpp b/subprojects/websocketpp/websocketpp/logger/stub.hpp new file mode 100644 index 000000000..2db6da7df --- /dev/null +++ b/subprojects/websocketpp/websocketpp/logger/stub.hpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_LOGGER_STUB_HPP +#define WEBSOCKETPP_LOGGER_STUB_HPP + +#include + +#include + +#include + +namespace websocketpp { +namespace log { + +/// Stub logger that ignores all input +class stub { +public: + /// Construct the logger + /** + * @param hint A channel type specific hint for how to construct the logger + */ + explicit stub(channel_type_hint::value) {} + + /// Construct the logger + /** + * @param default_channels A set of channels to statically enable + * @param hint A channel type specific hint for how to construct the logger + */ + stub(level, channel_type_hint::value) {} + _WEBSOCKETPP_CONSTEXPR_TOKEN_ stub() {} + + /// Dynamically enable the given list of channels + /** + * All operations on the stub logger are no-ops and all arguments are + * ignored + * + * @param channels The package of channels to enable + */ + void set_channels(level) {} + + /// Dynamically disable the given list of channels + /** + * All operations on the stub logger are no-ops and all arguments are + * ignored + * + * @param channels The package of channels to disable + */ + void clear_channels(level) {} + + /// Write a string message to the given channel + /** + * Writing on the stub logger is a no-op and all arguments are ignored + * + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level, std::string const &) {} + + /// Write a cstring message to the given channel + /** + * Writing on the stub logger is a no-op and all arguments are ignored + * + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level, char const *) {} + + /// Test whether a channel is statically enabled + /** + * The stub logger has no channels so all arguments are ignored and + * `static_test` always returns false. + * + * @param channel The package of channels to test + */ + _WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level) const { + return false; + } + + /// Test whether a channel is dynamically enabled + /** + * The stub logger has no channels so all arguments are ignored and + * `dynamic_test` always returns false. + * + * @param channel The package of channels to test + */ + bool dynamic_test(level) { + return false; + } +}; + +} // log +} // websocketpp + +#endif // WEBSOCKETPP_LOGGER_STUB_HPP diff --git a/subprojects/websocketpp/websocketpp/logger/syslog.hpp b/subprojects/websocketpp/websocketpp/logger/syslog.hpp new file mode 100644 index 000000000..513abee4a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/logger/syslog.hpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * The initial version of this logging policy was contributed to the WebSocket++ + * project by Tom Hughes. + */ + +#ifndef WEBSOCKETPP_LOGGER_SYSLOG_HPP +#define WEBSOCKETPP_LOGGER_SYSLOG_HPP + +#include + +#include + +#include +#include + +namespace websocketpp { +namespace log { + +/// Basic logger that outputs to syslog +template +class syslog : public basic { +public: + typedef basic base; + + /// Construct the logger + /** + * @param hint A channel type specific hint for how to construct the logger + */ + syslog(channel_type_hint::value hint = + channel_type_hint::access) + : basic(hint), m_channel_type_hint(hint) {} + + /// Construct the logger + /** + * @param channels A set of channels to statically enable + * @param hint A channel type specific hint for how to construct the logger + */ + syslog(level channels, channel_type_hint::value hint = + channel_type_hint::access) + : basic(channels, hint), m_channel_type_hint(hint) {} + + /// Write a string message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, std::string const & msg) { + write(channel, msg.c_str()); + } + + /// Write a cstring message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, char const * msg) { + scoped_lock_type lock(base::m_lock); + if (!this->dynamic_test(channel)) { return; } + ::syslog(syslog_priority(channel), "[%s] %s", + names::channel_name(channel), msg); + } +private: + typedef typename base::scoped_lock_type scoped_lock_type; + + /// The default level is used for all access logs and any error logs that + /// don't trivially map to one of the standard syslog levels. + static int const default_level = LOG_INFO; + + /// retrieve the syslog priority code given a WebSocket++ channel + /** + * @param channel The level to look up + * @return The syslog level associated with `channel` + */ + int syslog_priority(level channel) const { + if (m_channel_type_hint == channel_type_hint::access) { + return syslog_priority_access(channel); + } else { + return syslog_priority_error(channel); + } + } + + /// retrieve the syslog priority code given a WebSocket++ error channel + /** + * @param channel The level to look up + * @return The syslog level associated with `channel` + */ + int syslog_priority_error(level channel) const { + switch (channel) { + case elevel::devel: + return LOG_DEBUG; + case elevel::library: + return LOG_DEBUG; + case elevel::info: + return LOG_INFO; + case elevel::warn: + return LOG_WARNING; + case elevel::rerror: + return LOG_ERR; + case elevel::fatal: + return LOG_CRIT; + default: + return default_level; + } + } + + /// retrieve the syslog priority code given a WebSocket++ access channel + /** + * @param channel The level to look up + * @return The syslog level associated with `channel` + */ + _WEBSOCKETPP_CONSTEXPR_TOKEN_ int syslog_priority_access(level) const { + return default_level; + } + + channel_type_hint::value m_channel_type_hint; +}; + +} // log +} // websocketpp + +#endif // WEBSOCKETPP_LOGGER_SYSLOG_HPP diff --git a/subprojects/websocketpp/websocketpp/message_buffer/alloc.hpp b/subprojects/websocketpp/websocketpp/message_buffer/alloc.hpp new file mode 100644 index 000000000..75d89766a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/message_buffer/alloc.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP +#define WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP + +#include +#include + +namespace websocketpp { +namespace message_buffer { +namespace alloc { + +/// A connection message manager that allocates a new message for each +/// request. +template +class con_msg_manager + : public lib::enable_shared_from_this > +{ +public: + typedef con_msg_manager type; + typedef lib::shared_ptr ptr; + typedef lib::weak_ptr weak_ptr; + + typedef typename message::ptr message_ptr; + + /// Get an empty message buffer + /** + * @return A shared pointer to an empty new message + */ + message_ptr get_message() { + return message_ptr(lib::make_shared(type::shared_from_this())); + } + + /// Get a message buffer with specified size and opcode + /** + * @param op The opcode to use + * @param size Minimum size in bytes to request for the message payload. + * + * @return A shared pointer to a new message with specified size. + */ + message_ptr get_message(frame::opcode::value op,size_t size) { + return message_ptr(lib::make_shared(type::shared_from_this(),op,size)); + } + + /// Recycle a message + /** + * This method shouldn't be called. If it is, return false to indicate an + * error. The rest of the method recycle chain should notice this and free + * the memory. + * + * @param msg The message to be recycled. + * + * @return true if the message was successfully recycled, false otherwse. + */ + bool recycle(message *) { + return false; + } +}; + +/// An endpoint message manager that allocates a new manager for each +/// connection. +template +class endpoint_msg_manager { +public: + typedef typename con_msg_manager::ptr con_msg_man_ptr; + + /// Get a pointer to a connection message manager + /** + * @return A pointer to the requested connection message manager. + */ + con_msg_man_ptr get_manager() const { + return con_msg_man_ptr(lib::make_shared()); + } +}; + +} // namespace alloc +} // namespace message_buffer +} // namespace websocketpp + +#endif // WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP diff --git a/subprojects/websocketpp/websocketpp/message_buffer/message.hpp b/subprojects/websocketpp/websocketpp/message_buffer/message.hpp new file mode 100644 index 000000000..da36e2001 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/message_buffer/message.hpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP +#define WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP + +#include +#include + +#include + +namespace websocketpp { +namespace message_buffer { + +/* # message: + * object that stores a message while it is being sent or received. Contains + * the message payload itself, the message header, the extension data, and the + * opcode. + * + * # connection_message_manager: + * An object that manages all of the message_buffers associated with a given + * connection. Implements the get_message_buffer(size) method that returns + * a message buffer at least size bytes long. + * + * Message buffers are reference counted with shared ownership semantics. Once + * requested from the manager the requester and it's associated downstream code + * may keep a pointer to the message indefinitely at a cost of extra resource + * usage. Once the reference count drops to the point where the manager is the + * only reference the messages is recycled using whatever method is implemented + * in the manager. + * + * # endpoint_message_manager: + * An object that manages connection_message_managers. Implements the + * get_message_manager() method. This is used once by each connection to + * request the message manager that they are supposed to use to manage message + * buffers for their own use. + * + * TYPES OF CONNECTION_MESSAGE_MANAGERS + * - allocate a message with the exact size every time one is requested + * - maintain a pool of pre-allocated messages and return one when needed. + * Recycle previously used messages back into the pool + * + * TYPES OF ENDPOINT_MESSAGE_MANAGERS + * - allocate a new connection manager for each connection. Message pools + * become connection specific. This increases memory usage but improves + * concurrency. + * - allocate a single connection manager and share a pointer to it with all + * connections created by this endpoint. The message pool will be shared + * among all connections, improving memory usage and performance at the cost + * of reduced concurrency + */ + + +/// Represents a buffer for a single WebSocket message. +/** + * + * + */ +template class con_msg_manager> +class message { +public: + typedef lib::shared_ptr ptr; + + typedef con_msg_manager con_msg_man_type; + typedef typename con_msg_man_type::ptr con_msg_man_ptr; + typedef typename con_msg_man_type::weak_ptr con_msg_man_weak_ptr; + + /// Construct an empty message + /** + * Construct an empty message + */ + message(const con_msg_man_ptr manager) + : m_manager(manager) + , m_prepared(false) + , m_fin(true) + , m_terminal(false) + , m_compressed(false) {} + + /// Construct a message and fill in some values + /** + * + */ + message(const con_msg_man_ptr manager, frame::opcode::value op, size_t size = 128) + : m_manager(manager) + , m_opcode(op) + , m_prepared(false) + , m_fin(true) + , m_terminal(false) + , m_compressed(false) + { + m_payload.reserve(size); + } + + /// Return whether or not the message has been prepared for sending + /** + * The prepared flag indicates that the message has been prepared by a + * websocket protocol processor and is ready to be written to the wire. + * + * @return whether or not the message has been prepared for sending + */ + bool get_prepared() const { + return m_prepared; + } + + /// Set or clear the flag that indicates that the message has been prepared + /** + * This flag should not be set by end user code without a very good reason. + * + * @param value The value to set the prepared flag to + */ + void set_prepared(bool value) { + m_prepared = value; + } + + /// Return whether or not the message is flagged as compressed + /** + * @return whether or not the message is/should be compressed + */ + bool get_compressed() const { + return m_compressed; + } + + /// Set or clear the compression flag + /** + * Setting the compression flag indicates that the data in this message + * would benefit from compression. If both endpoints negotiate a compression + * extension WebSocket++ will attempt to compress messages with this flag. + * Setting this flag does not guarantee that the message will be compressed. + * + * @param value The value to set the compressed flag to + */ + void set_compressed(bool value) { + m_compressed = value; + } + + /// Get whether or not the message is terminal + /** + * Messages can be flagged as terminal, which results in the connection + * being close after they are written rather than the implementation going + * on to the next message in the queue. This is typically used internally + * for close messages only. + * + * @return Whether or not this message is marked terminal + */ + bool get_terminal() const { + return m_terminal; + } + + /// Set the terminal flag + /** + * This flag should not be set by end user code without a very good reason. + * + * @see get_terminal() + * + * @param value The value to set the terminal flag to. + */ + void set_terminal(bool value) { + m_terminal = value; + } + /// Read the fin bit + /** + * A message with the fin bit set will be sent as the last message of its + * sequence. A message with the fin bit cleared will require subsequent + * frames of opcode continuation until one of them has the fin bit set. + * + * The remote end likely will not deliver any bytes until the frame with the fin + * bit set has been received. + * + * @return Whether or not the fin bit is set + */ + bool get_fin() const { + return m_fin; + } + + /// Set the fin bit + /** + * @see get_fin for a more detailed explaination of the fin bit + * + * @param value The value to set the fin bit to. + */ + void set_fin(bool value) { + m_fin = value; + } + + /// Return the message opcode + frame::opcode::value get_opcode() const { + return m_opcode; + } + + /// Set the opcode + void set_opcode(frame::opcode::value op) { + m_opcode = op; + } + + /// Return the prepared frame header + /** + * This value is typically set by a websocket protocol processor + * and shouldn't be tampered with. + */ + std::string const & get_header() const { + return m_header; + } + + /// Set prepared frame header + /** + * Under normal circumstances this should not be called by end users + * + * @param header A string to set the header to. + */ + void set_header(std::string const & header) { + m_header = header; + } + + std::string const & get_extension_data() const { + return m_extension_data; + } + + /// Get a reference to the payload string + /** + * @return A const reference to the message's payload string + */ + std::string const & get_payload() const { + return m_payload; + } + + /// Get a non-const reference to the payload string + /** + * @return A reference to the message's payload string + */ + std::string & get_raw_payload() { + return m_payload; + } + + /// Set payload data + /** + * Set the message buffer's payload to the given value. + * + * @param payload A string to set the payload to. + */ + void set_payload(std::string const & payload) { + m_payload = payload; + } + + /// Set payload data + /** + * Set the message buffer's payload to the given value. + * + * @param payload A pointer to a data array to set to. + * @param len The length of new payload in bytes. + */ + void set_payload(void const * payload, size_t len) { + m_payload.reserve(len); + char const * pl = static_cast(payload); + m_payload.assign(pl, pl + len); + } + + /// Append payload data + /** + * Append data to the message buffer's payload. + * + * @param payload A string containing the data array to append. + */ + void append_payload(std::string const & payload) { + m_payload.append(payload); + } + + /// Append payload data + /** + * Append data to the message buffer's payload. + * + * @param payload A pointer to a data array to append + * @param len The length of payload in bytes + */ + void append_payload(void const * payload, size_t len) { + m_payload.reserve(m_payload.size()+len); + m_payload.append(static_cast(payload),len); + } + + /// Recycle the message + /** + * A request to recycle this message was received. Forward that request to + * the connection message manager for processing. Errors and exceptions + * from the manager's recycle member function should be passed back up the + * call chain. The caller to message::recycle will deal with them. + * + * Recycle must *only* be called by the message shared_ptr's destructor. + * Once recycled successfully, ownership of the memory has been passed to + * another system and must not be accessed again. + * + * @return true if the message was successfully recycled, false otherwise. + */ + bool recycle() { + con_msg_man_ptr shared = m_manager.lock(); + + if (shared) { + return shared->recycle(this); + } else { + return false; + } + } +private: + con_msg_man_weak_ptr m_manager; + std::string m_header; + std::string m_extension_data; + std::string m_payload; + frame::opcode::value m_opcode; + bool m_prepared; + bool m_fin; + bool m_terminal; + bool m_compressed; +}; + +} // namespace message_buffer +} // namespace websocketpp + +#endif // WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP diff --git a/subprojects/websocketpp/websocketpp/message_buffer/pool.hpp b/subprojects/websocketpp/websocketpp/message_buffer/pool.hpp new file mode 100644 index 000000000..3af9e02a2 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/message_buffer/pool.hpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP +#define WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP + +#include + +#include + +namespace websocketpp { +namespace message_buffer { + +/* # message: + * object that stores a message while it is being sent or received. Contains + * the message payload itself, the message header, the extension data, and the + * opcode. + * + * # connection_message_manager: + * An object that manages all of the message_buffers associated with a given + * connection. Implements the get_message_buffer(size) method that returns + * a message buffer at least size bytes long. + * + * Message buffers are reference counted with shared ownership semantics. Once + * requested from the manager the requester and it's associated downstream code + * may keep a pointer to the message indefinitely at a cost of extra resource + * usage. Once the reference count drops to the point where the manager is the + * only reference the messages is recycled using whatever method is implemented + * in the manager. + * + * # endpoint_message_manager: + * An object that manages connection_message_managers. Implements the + * get_message_manager() method. This is used once by each connection to + * request the message manager that they are supposed to use to manage message + * buffers for their own use. + * + * TYPES OF CONNECTION_MESSAGE_MANAGERS + * - allocate a message with the exact size every time one is requested + * - maintain a pool of pre-allocated messages and return one when needed. + * Recycle previously used messages back into the pool + * + * TYPES OF ENDPOINT_MESSAGE_MANAGERS + * - allocate a new connection manager for each connection. Message pools + * become connection specific. This increases memory usage but improves + * concurrency. + * - allocate a single connection manager and share a pointer to it with all + * connections created by this endpoint. The message pool will be shared + * among all connections, improving memory usage and performance at the cost + * of reduced concurrency + */ + +/// Custom deleter for use in shared_ptrs to message. +/** + * This is used to catch messages about to be deleted and offer the manager the + * ability to recycle them instead. Message::recycle will return true if it was + * successfully recycled and false otherwise. In the case of exceptions or error + * this deleter frees the memory. + */ +template +void message_deleter(T* msg) { + try { + if (!msg->recycle()) { + delete msg; + } + } catch (...) { + // TODO: is there a better way to ensure this function doesn't throw? + delete msg; + } +} + +/// Represents a buffer for a single WebSocket message. +/** + * + * + */ +template +class message { +public: + typedef lib::shared_ptr ptr; + + typedef typename con_msg_manager::weak_ptr con_msg_man_ptr; + + message(con_msg_man_ptr manager, size_t size = 128) + : m_manager(manager) + , m_payload(size) {} + + frame::opcode::value get_opcode() const { + return m_opcode; + } + const std::string& get_header() const { + return m_header; + } + const std::string& get_extension_data() const { + return m_extension_data; + } + const std::string& get_payload() const { + return m_payload; + } + + /// Recycle the message + /** + * A request to recycle this message was received. Forward that request to + * the connection message manager for processing. Errors and exceptions + * from the manager's recycle member function should be passed back up the + * call chain. The caller to message::recycle will deal with them. + * + * Recycle must *only* be called by the message shared_ptr's destructor. + * Once recycled successfully, ownership of the memory has been passed to + * another system and must not be accessed again. + * + * @return true if the message was successfully recycled, false otherwise. + */ + bool recycle() { + typename con_msg_manager::ptr shared = m_manager.lock(); + + if (shared) { + return shared->(recycle(this)); + } else { + return false; + } + } +private: + con_msg_man_ptr m_manager; + + frame::opcode::value m_opcode; + std::string m_header; + std::string m_extension_data; + std::string m_payload; +}; + +namespace alloc { + +/// A connection message manager that allocates a new message for each +/// request. +template +class con_msg_manager { +public: + typedef lib::shared_ptr ptr; + typedef lib::weak_ptr weak_ptr; + + typedef typename message::ptr message_ptr; + + /// Get a message buffer with specified size + /** + * @param size Minimum size in bytes to request for the message payload. + * + * @return A shared pointer to a new message with specified size. + */ + message_ptr get_message(size_t size) const { + return lib::make_shared(size); + } + + /// Recycle a message + /** + * This method shouldn't be called. If it is, return false to indicate an + * error. The rest of the method recycle chain should notice this and free + * the memory. + * + * @param msg The message to be recycled. + * + * @return true if the message was successfully recycled, false otherwse. + */ + bool recycle(message * msg) { + return false; + } +}; + +/// An endpoint message manager that allocates a new manager for each +/// connection. +template +class endpoint_msg_manager { +public: + typedef typename con_msg_manager::ptr con_msg_man_ptr; + + /// Get a pointer to a connection message manager + /** + * @return A pointer to the requested connection message manager. + */ + con_msg_man_ptr get_manager() const { + return lib::make_shared(); + } +}; + +} // namespace alloc + +namespace pool { + +/// A connection messages manager that maintains a pool of messages that is +/// used to fulfill get_message requests. +class con_msg_manager { + +}; + +/// An endpoint manager that maintains a shared pool of connection managers +/// and returns an appropriate one for the requesting connection. +class endpoint_msg_manager { + +}; + +} // namespace pool + +} // namespace message_buffer +} // namespace websocketpp + +#endif // WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP diff --git a/subprojects/websocketpp/websocketpp/processors/base.hpp b/subprojects/websocketpp/websocketpp/processors/base.hpp new file mode 100644 index 000000000..4c123dfab --- /dev/null +++ b/subprojects/websocketpp/websocketpp/processors/base.hpp @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_BASE_HPP +#define WEBSOCKETPP_PROCESSOR_BASE_HPP + +#include +#include +#include + +#include +#include + +#include + +namespace websocketpp { +namespace processor { + +/// Constants related to processing WebSocket connections +namespace constants { + +static char const upgrade_token[] = "websocket"; +static char const connection_token[] = "Upgrade"; +static char const handshake_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +} // namespace constants + + +/// Processor class related error codes +namespace error_cat { +enum value { + BAD_REQUEST = 0, // Error was the result of improperly formatted user input + INTERNAL_ERROR = 1, // Error was a logic error internal to WebSocket++ + PROTOCOL_VIOLATION = 2, + MESSAGE_TOO_BIG = 3, + PAYLOAD_VIOLATION = 4 // Error was due to receiving invalid payload data +}; +} // namespace error_cat + +/// Error code category and codes used by all processor types +namespace error { +enum processor_errors { + /// Catch-all error for processor policy errors that don't fit in other + /// categories + general = 1, + + /// Error was the result of improperly formatted user input + bad_request, + + /// Processor encountered a protocol violation in an incoming message + protocol_violation, + + /// Processor encountered a message that was too large + message_too_big, + + /// Processor encountered invalid payload data. + invalid_payload, + + /// The processor method was called with invalid arguments + invalid_arguments, + + /// Opcode was invalid for requested operation + invalid_opcode, + + /// Control frame too large + control_too_big, + + /// Illegal use of reserved bit + invalid_rsv_bit, + + /// Fragmented control message + fragmented_control, + + /// Continuation without message + invalid_continuation, + + /// Clients may not send unmasked frames + masking_required, + + /// Servers may not send masked frames + masking_forbidden, + + /// Payload length not minimally encoded + non_minimal_encoding, + + /// Not supported on 32 bit systems + requires_64bit, + + /// Invalid UTF-8 encoding + invalid_utf8, + + /// Operation required not implemented functionality + not_implemented, + + /// Invalid HTTP method + invalid_http_method, + + /// Invalid HTTP version + invalid_http_version, + + /// Invalid HTTP status + invalid_http_status, + + /// Missing Required Header + missing_required_header, + + /// Embedded SHA-1 library error + sha1_library, + + /// No support for this feature in this protocol version. + no_protocol_support, + + /// Reserved close code used + reserved_close_code, + + /// Invalid close code used + invalid_close_code, + + /// Using a reason requires a close code + reason_requires_code, + + /// Error parsing subprotocols + subprotocol_parse_error, + + /// Error parsing extensions + extension_parse_error, + + /// Extension related operation was ignored because extensions are disabled + extensions_disabled, + + /// Short Ke3 read. Hybi00 requires a third key to be read from the 8 bytes + /// after the handshake. Less than 8 bytes were read. + short_key3 +}; + +/// Category for processor errors +class processor_category : public lib::error_category { +public: + processor_category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.processor"; + } + + std::string message(int value) const { + switch(value) { + case error::general: + return "Generic processor error"; + case error::bad_request: + return "invalid user input"; + case error::protocol_violation: + return "Generic protocol violation"; + case error::message_too_big: + return "A message was too large"; + case error::invalid_payload: + return "A payload contained invalid data"; + case error::invalid_arguments: + return "invalid function arguments"; + case error::invalid_opcode: + return "invalid opcode"; + case error::control_too_big: + return "Control messages are limited to fewer than 125 characters"; + case error::invalid_rsv_bit: + return "Invalid use of reserved bits"; + case error::fragmented_control: + return "Control messages cannot be fragmented"; + case error::invalid_continuation: + return "Invalid message continuation"; + case error::masking_required: + return "Clients may not send unmasked frames"; + case error::masking_forbidden: + return "Servers may not send masked frames"; + case error::non_minimal_encoding: + return "Payload length was not minimally encoded"; + case error::requires_64bit: + return "64 bit frames are not supported on 32 bit systems"; + case error::invalid_utf8: + return "Invalid UTF8 encoding"; + case error::not_implemented: + return "Operation required not implemented functionality"; + case error::invalid_http_method: + return "Invalid HTTP method."; + case error::invalid_http_version: + return "Invalid HTTP version."; + case error::invalid_http_status: + return "Invalid HTTP status."; + case error::missing_required_header: + return "A required HTTP header is missing"; + case error::sha1_library: + return "SHA-1 library error"; + case error::no_protocol_support: + return "The WebSocket protocol version in use does not support this feature"; + case error::reserved_close_code: + return "Reserved close code used"; + case error::invalid_close_code: + return "Invalid close code used"; + case error::reason_requires_code: + return "Using a close reason requires a valid close code"; + case error::subprotocol_parse_error: + return "Error parsing subprotocol header"; + case error::extension_parse_error: + return "Error parsing extension header"; + case error::extensions_disabled: + return "Extensions are disabled"; + case error::short_key3: + return "Short Hybi00 Key 3 read"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the processor error category +inline lib::error_category const & get_processor_category() { + static processor_category instance; + return instance; +} + +/// Create an error code with the given value and the processor category +inline lib::error_code make_error_code(error::processor_errors e) { + return lib::error_code(static_cast(e), get_processor_category()); +} + +/// Converts a processor error_code into a websocket close code +/** + * Looks up the appropriate WebSocket close code that should be sent after an + * error of this sort occurred. + * + * If the error is not in the processor category close::status::blank is + * returned. + * + * If the error isn't normally associated with reasons to close a connection + * (such as errors intended to be used internally or delivered to client + * applications, ex: invalid arguments) then + * close::status::internal_endpoint_error is returned. + */ +inline close::status::value to_ws(lib::error_code ec) { + if (ec.category() != get_processor_category()) { + return close::status::blank; + } + + switch (ec.value()) { + case error::protocol_violation: + case error::control_too_big: + case error::invalid_opcode: + case error::invalid_rsv_bit: + case error::fragmented_control: + case error::invalid_continuation: + case error::masking_required: + case error::masking_forbidden: + case error::reserved_close_code: + case error::invalid_close_code: + return close::status::protocol_error; + case error::invalid_payload: + case error::invalid_utf8: + return close::status::invalid_payload; + case error::message_too_big: + return close::status::message_too_big; + default: + return close::status::internal_endpoint_error; + } +} + +} // namespace error +} // namespace processor +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif //WEBSOCKETPP_PROCESSOR_BASE_HPP diff --git a/subprojects/websocketpp/websocketpp/processors/hybi00.hpp b/subprojects/websocketpp/websocketpp/processors/hybi00.hpp new file mode 100644 index 000000000..95ad9dfa8 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/processors/hybi00.hpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI00_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi Draft version 00 +/** + * There are many differences between Hybi 00 and Hybi 13 + */ +template +class hybi00 : public processor { +public: + typedef processor base; + + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + + typedef typename config::message_type message_type; + typedef typename message_type::ptr message_ptr; + + typedef typename config::con_msg_manager_type::ptr msg_manager_ptr; + + explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager) + : processor(secure, p_is_server) + , msg_hdr(0x00) + , msg_ftr(0xff) + , m_state(HEADER) + , m_msg_manager(manager) {} + + int get_version() const { + return 0; + } + + lib::error_code validate_handshake(request_type const & r) const { + if (r.get_method() != "GET") { + return make_error_code(error::invalid_http_method); + } + + if (r.get_version() != "HTTP/1.1") { + return make_error_code(error::invalid_http_version); + } + + // required headers + // Host is required by HTTP/1.1 + // Connection is required by is_websocket_handshake + // Upgrade is required by is_websocket_handshake + if (r.get_header("Sec-WebSocket-Key1").empty() || + r.get_header("Sec-WebSocket-Key2").empty() || + r.get_header("Sec-WebSocket-Key3").empty()) + { + return make_error_code(error::missing_required_header); + } + + return lib::error_code(); + } + + lib::error_code process_handshake(request_type const & req, + std::string const & subprotocol, response_type & res) const + { + char key_final[16]; + + // copy key1 into final key + decode_client_key(req.get_header("Sec-WebSocket-Key1"), &key_final[0]); + + // copy key2 into final key + decode_client_key(req.get_header("Sec-WebSocket-Key2"), &key_final[4]); + + // copy key3 into final key + // key3 should be exactly 8 bytes. If it is more it will be truncated + // if it is less the final key will almost certainly be wrong. + // TODO: decide if it is best to silently fail here or produce some sort + // of warning or exception. + std::string const & key3 = req.get_header("Sec-WebSocket-Key3"); + std::copy(key3.c_str(), + key3.c_str()+(std::min)(static_cast(8), key3.size()), + &key_final[8]); + + res.append_header( + "Sec-WebSocket-Key3", + md5::md5_hash_string(std::string(key_final,16)) + ); + + res.append_header("Upgrade","WebSocket"); + res.append_header("Connection","Upgrade"); + + // Echo back client's origin unless our local application set a + // more restrictive one. + if (res.get_header("Sec-WebSocket-Origin").empty()) { + res.append_header("Sec-WebSocket-Origin",req.get_header("Origin")); + } + + // Echo back the client's request host unless our local application + // set a different one. + if (res.get_header("Sec-WebSocket-Location").empty()) { + uri_ptr uri = get_uri(req); + res.append_header("Sec-WebSocket-Location",uri->str()); + } + + if (!subprotocol.empty()) { + res.replace_header("Sec-WebSocket-Protocol",subprotocol); + } + + return lib::error_code(); + } + + /// Fill in a set of request headers for a client connection request + /** + * The Hybi 00 processor only implements incoming connections so this will + * always return an error. + * + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type &, uri_ptr, + std::vector const &) const + { + return error::make_error_code(error::no_protocol_support); + } + + /// Validate the server's response to an outgoing handshake request + /** + * The Hybi 00 processor only implements incoming connections so this will + * always return an error. + * + * @param req The original request sent + * @param res The reponse to generate + * @return An error code, 0 on success, non-zero for other errors + */ + lib::error_code validate_server_handshake_response(request_type const &, + response_type &) const + { + return error::make_error_code(error::no_protocol_support); + } + + std::string get_raw(response_type const & res) const { + response_type temp = res; + temp.remove_header("Sec-WebSocket-Key3"); + return temp.raw() + res.get_header("Sec-WebSocket-Key3"); + } + + std::string const & get_origin(request_type const & r) const { + return r.get_header("Origin"); + } + + /// Extracts requested subprotocols from a handshake request + /** + * hybi00 does support subprotocols + * https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-1.9 + * + * @param [in] req The request to extract from + * @param [out] subprotocol_list A reference to a vector of strings to store + * the results in. + */ + lib::error_code extract_subprotocols(request_type const & req, + std::vector & subprotocol_list) + { + if (!req.get_header("Sec-WebSocket-Protocol").empty()) { + http::parameter_list p; + + if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) { + http::parameter_list::const_iterator it; + + for (it = p.begin(); it != p.end(); ++it) { + subprotocol_list.push_back(it->first); + } + } else { + return error::make_error_code(error::subprotocol_parse_error); + } + } + return lib::error_code(); + } + + uri_ptr get_uri(request_type const & request) const { + std::string h = request.get_header("Host"); + + size_t last_colon = h.rfind(":"); + size_t last_sbrace = h.rfind("]"); + + // no : = hostname with no port + // last : before ] = ipv6 literal with no port + // : with no ] = hostname with port + // : after ] = ipv6 literal with port + + if (last_colon == std::string::npos || + (last_sbrace != std::string::npos && last_sbrace > last_colon)) + { + return lib::make_shared(base::m_secure, h, request.get_uri()); + } else { + return lib::make_shared(base::m_secure, + h.substr(0,last_colon), + h.substr(last_colon+1), + request.get_uri()); + } + + // TODO: check if get_uri is a full uri + } + + /// Get hybi00 handshake key3 + /** + * @todo This doesn't appear to be used anymore. It might be able to be + * removed + */ + std::string get_key3() const { + return ""; + } + + /// Process new websocket connection bytes + size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) { + // if in state header we are expecting a 0x00 byte, if we don't get one + // it is a fatal error + size_t p = 0; // bytes processed + size_t l = 0; + + ec = lib::error_code(); + + while (p < len) { + if (m_state == HEADER) { + if (buf[p] == msg_hdr) { + p++; + m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1); + + if (!m_msg_ptr) { + ec = make_error_code(websocketpp::error::no_incoming_buffers); + m_state = FATAL_ERROR; + } else { + m_state = PAYLOAD; + } + } else { + ec = make_error_code(error::protocol_violation); + m_state = FATAL_ERROR; + } + } else if (m_state == PAYLOAD) { + uint8_t *it = std::find(buf+p,buf+len,msg_ftr); + + // 0 1 2 3 4 5 + // 0x00 0x23 0x23 0x23 0xff 0xXX + + // Copy payload bytes into message + l = static_cast(it-(buf+p)); + m_msg_ptr->append_payload(buf+p,l); + p += l; + + if (it != buf+len) { + // message is done, copy it and the trailing + p++; + // TODO: validation + m_state = READY; + } + } else { + // TODO + break; + } + } + // If we get one, we create a new message and move to application state + + // if in state application we are copying bytes into the output message + // and validating them for UTF8 until we hit a 0xff byte. Once we hit + // 0x00, the message is complete and is dispatched. Then we go back to + // header state. + + //ec = make_error_code(error::not_implemented); + return p; + } + + bool ready() const { + return (m_state == READY); + } + + bool get_error() const { + return false; + } + + message_ptr get_message() { + message_ptr ret = m_msg_ptr; + m_msg_ptr = message_ptr(); + m_state = HEADER; + return ret; + } + + /// Prepare a message for writing + /** + * Performs validation, masking, compression, etc. will return an error if + * there was an error, otherwise msg will be ready to be written + */ + virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) + { + if (!in || !out) { + return make_error_code(error::invalid_arguments); + } + + // TODO: check if the message is prepared already + + // validate opcode + if (in->get_opcode() != frame::opcode::text) { + return make_error_code(error::invalid_opcode); + } + + std::string& i = in->get_raw_payload(); + //std::string& o = out->get_raw_payload(); + + // validate payload utf8 + if (!utf8_validator::validate(i)) { + return make_error_code(error::invalid_payload); + } + + // generate header + out->set_header(std::string(reinterpret_cast(&msg_hdr),1)); + + // process payload + out->set_payload(i); + out->append_payload(std::string(reinterpret_cast(&msg_ftr),1)); + + // hybi00 doesn't support compression + // hybi00 doesn't have masking + + out->set_prepared(true); + + return lib::error_code(); + } + + /// Prepare a ping frame + /** + * Hybi 00 doesn't support pings so this will always return an error + * + * @param in The string to use for the ping payload + * @param out The message buffer to prepare the ping in. + * @return Status code, zero on success, non-zero on failure + */ + lib::error_code prepare_ping(std::string const &, message_ptr) const + { + return lib::error_code(error::no_protocol_support); + } + + /// Prepare a pong frame + /** + * Hybi 00 doesn't support pongs so this will always return an error + * + * @param in The string to use for the pong payload + * @param out The message buffer to prepare the pong in. + * @return Status code, zero on success, non-zero on failure + */ + lib::error_code prepare_pong(std::string const &, message_ptr) const + { + return lib::error_code(error::no_protocol_support); + } + + /// Prepare a close frame + /** + * Hybi 00 doesn't support the close code or reason so these parameters are + * ignored. + * + * @param code The close code to send + * @param reason The reason string to send + * @param out The message buffer to prepare the fame in + * @return Status code, zero on success, non-zero on failure + */ + lib::error_code prepare_close(close::status::value, std::string const &, + message_ptr out) const + { + if (!out) { + return lib::error_code(error::invalid_arguments); + } + + std::string val; + val.append(1,'\xff'); + val.append(1,'\x00'); + out->set_payload(val); + out->set_prepared(true); + + return lib::error_code(); + } +private: + void decode_client_key(std::string const & key, char * result) const { + unsigned int spaces = 0; + std::string digits; + uint32_t num; + + // key2 + for (size_t i = 0; i < key.size(); i++) { + if (key[i] == ' ') { + spaces++; + } else if (key[i] >= '0' && key[i] <= '9') { + digits += key[i]; + } + } + + num = static_cast(strtoul(digits.c_str(), NULL, 10)); + if (spaces > 0 && num > 0) { + num = htonl(num/spaces); + std::copy(reinterpret_cast(&num), + reinterpret_cast(&num)+4, + result); + } else { + std::fill(result,result+4,0); + } + } + + enum state { + HEADER = 0, + PAYLOAD = 1, + READY = 2, + FATAL_ERROR = 3 + }; + + uint8_t const msg_hdr; + uint8_t const msg_ftr; + + state m_state; + + msg_manager_ptr m_msg_manager; + message_ptr m_msg_ptr; + utf8_validator::validator m_validator; +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI00_HPP diff --git a/subprojects/websocketpp/websocketpp/processors/hybi07.hpp b/subprojects/websocketpp/websocketpp/processors/hybi07.hpp new file mode 100644 index 000000000..14b67c21a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/processors/hybi07.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI07_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI07_HPP + +#include + +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi Draft version 07 +/** + * The primary difference between 07 and 08 is a version number. + */ +template +class hybi07 : public hybi08 { +public: + typedef typename config::request_type request_type; + + typedef typename config::con_msg_manager_type::ptr msg_manager_ptr; + typedef typename config::rng_type rng_type; + + explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : hybi08(secure, p_is_server, manager, rng) {} + + /// Fill in a set of request headers for a client connection request + /** + * The Hybi 07 processor only implements incoming connections so this will + * always return an error. + * + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type &, uri_ptr, + std::vector const &) const + { + return error::make_error_code(error::no_protocol_support); + } + + int get_version() const { + return 7; + } +private: +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI07_HPP diff --git a/subprojects/websocketpp/websocketpp/processors/hybi08.hpp b/subprojects/websocketpp/websocketpp/processors/hybi08.hpp new file mode 100644 index 000000000..15f6e6586 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/processors/hybi08.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI08_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI08_HPP + +#include + +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi Draft version 08 +/** + * The primary difference between 08 and 13 is a different origin header name + */ +template +class hybi08 : public hybi13 { +public: + typedef hybi08 type; + typedef typename config::request_type request_type; + + typedef typename config::con_msg_manager_type::ptr msg_manager_ptr; + typedef typename config::rng_type rng_type; + + explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : hybi13(secure, p_is_server, manager, rng) {} + + /// Fill in a set of request headers for a client connection request + /** + * The Hybi 08 processor only implements incoming connections so this will + * always return an error. + * + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type &, uri_ptr, + std::vector const &) const + { + return error::make_error_code(error::no_protocol_support); + } + + int get_version() const { + return 8; + } + + std::string const & get_origin(request_type const & r) const { + return r.get_header("Sec-WebSocket-Origin"); + } +private: +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI08_HPP diff --git a/subprojects/websocketpp/websocketpp/processors/hybi13.hpp b/subprojects/websocketpp/websocketpp/processors/hybi13.hpp new file mode 100644 index 000000000..ca1243926 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/processors/hybi13.hpp @@ -0,0 +1,1078 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI13_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI13_HPP + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi version 13 (RFC6455) +template +class hybi13 : public processor { +public: + typedef processor base; + + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + + typedef typename config::message_type message_type; + typedef typename message_type::ptr message_ptr; + + typedef typename config::con_msg_manager_type msg_manager_type; + typedef typename msg_manager_type::ptr msg_manager_ptr; + typedef typename config::rng_type rng_type; + + typedef typename config::permessage_deflate_type permessage_deflate_type; + + typedef std::pair err_str_pair; + + explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : processor(secure, p_is_server) + , m_msg_manager(manager) + , m_rng(rng) + { + reset_headers(); + } + + int get_version() const { + return 13; + } + + bool has_permessage_deflate() const { + return m_permessage_deflate.is_implemented(); + } + + err_str_pair negotiate_extensions(request_type const & request) { + return negotiate_extensions_helper(request); + } + + err_str_pair negotiate_extensions(response_type const & response) { + return negotiate_extensions_helper(response); + } + + /// Extension negotiation helper function + /** + * This exists mostly because the code for requests and responses is + * identical and I can't have virtual template methods. + */ + template + err_str_pair negotiate_extensions_helper(header_type const & header) { + err_str_pair ret; + + // Respect blanket disabling of all extensions and don't even parse + // the extension header + if (!config::enable_extensions) { + ret.first = make_error_code(error::extensions_disabled); + return ret; + } + + http::parameter_list p; + + bool error = header.get_header_as_plist("Sec-WebSocket-Extensions",p); + + if (error) { + ret.first = make_error_code(error::extension_parse_error); + return ret; + } + + // If there are no extensions parsed then we are done! + if (p.size() == 0) { + return ret; + } + + http::parameter_list::const_iterator it; + + // look through the list of extension requests to find the first + // one that we can accept. + if (m_permessage_deflate.is_implemented()) { + err_str_pair neg_ret; + for (it = p.begin(); it != p.end(); ++it) { + // not a permessage-deflate extension request, ignore + if (it->first != "permessage-deflate") { + continue; + } + + // if we have already successfully negotiated this extension + // then skip any other requests to negotiate the same one + // with different parameters + if (m_permessage_deflate.is_enabled()) { + continue; + } + + // attempt to negotiate this offer + neg_ret = m_permessage_deflate.negotiate(it->second); + + if (neg_ret.first) { + // negotiation offer failed. Do nothing. We will continue + // searching for a permessage-deflate config that succeeds + continue; + } + + // Negotiation tentatively succeeded + + // Actually try to initialize the extension before we + // deem negotiation complete + lib::error_code ec = m_permessage_deflate.init(base::m_server); + + if (ec) { + // Negotiation succeeded but initialization failed this is + // an error that should stop negotiation of permessage + // deflate. Return the reason for the init failure + + ret.first = ec; + break; + } else { + // Successfully initialized, push the negotiated response into + // the reply and stop looking for additional permessage-deflate + // extensions + ret.second += neg_ret.second; + break; + } + } + } + + // support for future extensions would go here. Should check the value of + // ret.first before continuing. Might need to consider whether failure of + // negotiation of an earlier extension should stop negotiation of subsequent + // ones + + return ret; + } + + lib::error_code validate_handshake(request_type const & r) const { + if (r.get_method() != "GET") { + return make_error_code(error::invalid_http_method); + } + + if (r.get_version() != "HTTP/1.1") { + return make_error_code(error::invalid_http_version); + } + + // required headers + // Host is required by HTTP/1.1 + // Connection is required by is_websocket_handshake + // Upgrade is required by is_websocket_handshake + if (r.get_header("Sec-WebSocket-Key").empty()) { + return make_error_code(error::missing_required_header); + } + + return lib::error_code(); + } + + /* TODO: the 'subprotocol' parameter may need to be expanded into a more + * generic struct if other user input parameters to the processed handshake + * are found. + */ + lib::error_code process_handshake(request_type const & request, + std::string const & subprotocol, response_type & response) const + { + std::string server_key = request.get_header("Sec-WebSocket-Key"); + + lib::error_code ec = process_handshake_key(server_key); + + if (ec) { + return ec; + } + + response.replace_header("Sec-WebSocket-Accept",server_key); + response.append_header("Upgrade",constants::upgrade_token); + response.append_header("Connection",constants::connection_token); + + if (!subprotocol.empty()) { + response.replace_header("Sec-WebSocket-Protocol",subprotocol); + } + + return lib::error_code(); + } + + /// Fill in a set of request headers for a client connection request + /** + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type & req, uri_ptr + uri, std::vector const & subprotocols) const + { + req.set_method("GET"); + req.set_uri(uri->get_resource()); + req.set_version("HTTP/1.1"); + + req.append_header("Upgrade","websocket"); + req.append_header("Connection","Upgrade"); + req.replace_header("Sec-WebSocket-Version","13"); + req.replace_header("Host",uri->get_host_port()); + + if (!subprotocols.empty()) { + std::ostringstream result; + std::vector::const_iterator it = subprotocols.begin(); + result << *it++; + while (it != subprotocols.end()) { + result << ", " << *it++; + } + + req.replace_header("Sec-WebSocket-Protocol",result.str()); + } + + // Generate handshake key + frame::uint32_converter conv; + unsigned char raw_key[16]; + + for (int i = 0; i < 4; i++) { + conv.i = m_rng(); + std::copy(conv.c,conv.c+4,&raw_key[i*4]); + } + + req.replace_header("Sec-WebSocket-Key",base64_encode(raw_key, 16)); + + if (m_permessage_deflate.is_implemented()) { + std::string offer = m_permessage_deflate.generate_offer(); + if (!offer.empty()) { + req.replace_header("Sec-WebSocket-Extensions",offer); + } + } + + return lib::error_code(); + } + + /// Validate the server's response to an outgoing handshake request + /** + * @param req The original request sent + * @param res The reponse to generate + * @return An error code, 0 on success, non-zero for other errors + */ + lib::error_code validate_server_handshake_response(request_type const & req, + response_type& res) const + { + // A valid response has an HTTP 101 switching protocols code + if (res.get_status_code() != http::status_code::switching_protocols) { + return error::make_error_code(error::invalid_http_status); + } + + // And the upgrade token in an upgrade header + std::string const & upgrade_header = res.get_header("Upgrade"); + if (utility::ci_find_substr(upgrade_header, constants::upgrade_token, + sizeof(constants::upgrade_token)-1) == upgrade_header.end()) + { + return error::make_error_code(error::missing_required_header); + } + + // And the websocket token in the connection header + std::string const & con_header = res.get_header("Connection"); + if (utility::ci_find_substr(con_header, constants::connection_token, + sizeof(constants::connection_token)-1) == con_header.end()) + { + return error::make_error_code(error::missing_required_header); + } + + // And has a valid Sec-WebSocket-Accept value + std::string key = req.get_header("Sec-WebSocket-Key"); + lib::error_code ec = process_handshake_key(key); + + if (ec || key != res.get_header("Sec-WebSocket-Accept")) { + return error::make_error_code(error::missing_required_header); + } + + // check extensions + + return lib::error_code(); + } + + std::string get_raw(response_type const & res) const { + return res.raw(); + } + + std::string const & get_origin(request_type const & r) const { + return r.get_header("Origin"); + } + + lib::error_code extract_subprotocols(request_type const & req, + std::vector & subprotocol_list) + { + if (!req.get_header("Sec-WebSocket-Protocol").empty()) { + http::parameter_list p; + + if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) { + http::parameter_list::const_iterator it; + + for (it = p.begin(); it != p.end(); ++it) { + subprotocol_list.push_back(it->first); + } + } else { + return error::make_error_code(error::subprotocol_parse_error); + } + } + return lib::error_code(); + } + + uri_ptr get_uri(request_type const & request) const { + return get_uri_from_host(request,(base::m_secure ? "wss" : "ws")); + } + + /// Process new websocket connection bytes + /** + * + * Hybi 13 data streams represent a series of variable length frames. Each + * frame is made up of a series of fixed length fields. The lengths of later + * fields are contained in earlier fields. The first field length is fixed + * by the spec. + * + * This processor represents a state machine that keeps track of what field + * is presently being read and how many more bytes are needed to complete it + * + * + * + * + * Read two header bytes + * Extract full frame length. + * Read extra header bytes + * Validate frame header (including extension validate) + * Read extension data into extension message state object + * Read payload data into payload + * + * @param buf Input buffer + * + * @param len Length of input buffer + * + * @return Number of bytes processed or zero on error + */ + size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) { + size_t p = 0; + + ec = lib::error_code(); + + //std::cout << "consume: " << utility::to_hex(buf,len) << std::endl; + + // Loop while we don't have a message ready and we still have bytes + // left to process. + while (m_state != READY && m_state != FATAL_ERROR && + (p < len || m_bytes_needed == 0)) + { + if (m_state == HEADER_BASIC) { + p += this->copy_basic_header_bytes(buf+p,len-p); + + if (m_bytes_needed > 0) { + continue; + } + + ec = this->validate_incoming_basic_header( + m_basic_header, base::m_server, !m_data_msg.msg_ptr + ); + if (ec) {break;} + + // extract full header size and adjust consume state accordingly + m_state = HEADER_EXTENDED; + m_cursor = 0; + m_bytes_needed = frame::get_header_len(m_basic_header) - + frame::BASIC_HEADER_LENGTH; + } else if (m_state == HEADER_EXTENDED) { + p += this->copy_extended_header_bytes(buf+p,len-p); + + if (m_bytes_needed > 0) { + continue; + } + + ec = validate_incoming_extended_header(m_basic_header,m_extended_header); + if (ec){break;} + + m_state = APPLICATION; + m_bytes_needed = static_cast(get_payload_size(m_basic_header,m_extended_header)); + + // check if this frame is the start of a new message and set up + // the appropriate message metadata. + frame::opcode::value op = frame::get_opcode(m_basic_header); + + // TODO: get_message failure conditions + + if (frame::opcode::is_control(op)) { + m_control_msg = msg_metadata( + m_msg_manager->get_message(op,m_bytes_needed), + frame::get_masking_key(m_basic_header,m_extended_header) + ); + + m_current_msg = &m_control_msg; + } else { + if (!m_data_msg.msg_ptr) { + if (m_bytes_needed > base::m_max_message_size) { + ec = make_error_code(error::message_too_big); + break; + } + + m_data_msg = msg_metadata( + m_msg_manager->get_message(op,m_bytes_needed), + frame::get_masking_key(m_basic_header,m_extended_header) + ); + + if (m_permessage_deflate.is_enabled()) { + m_data_msg.msg_ptr->set_compressed(frame::get_rsv1(m_basic_header)); + } + } else { + // Fetch the underlying payload buffer from the data message we + // are writing into. + std::string & out = m_data_msg.msg_ptr->get_raw_payload(); + + if (out.size() + m_bytes_needed > base::m_max_message_size) { + ec = make_error_code(error::message_too_big); + break; + } + + // Each frame starts a new masking key. All other state + // remains between frames. + m_data_msg.prepared_key = prepare_masking_key( + frame::get_masking_key( + m_basic_header, + m_extended_header + ) + ); + + out.reserve(out.size() + m_bytes_needed); + } + m_current_msg = &m_data_msg; + } + } else if (m_state == EXTENSION) { + m_state = APPLICATION; + } else if (m_state == APPLICATION) { + size_t bytes_to_process = (std::min)(m_bytes_needed,len-p); + + if (bytes_to_process > 0) { + p += this->process_payload_bytes(buf+p,bytes_to_process,ec); + + if (ec) {break;} + } + + if (m_bytes_needed > 0) { + continue; + } + + // If this was the last frame in the message set the ready flag. + // Otherwise, reset processor state to read additional frames. + if (frame::get_fin(m_basic_header)) { + ec = finalize_message(); + if (ec) { + break; + } + } else { + this->reset_headers(); + } + } else { + // shouldn't be here + ec = make_error_code(error::general); + return 0; + } + } + + return p; + } + + /// Perform any finalization actions on an incoming message + /** + * Called after the full message is received. Provides the opportunity for + * extensions to complete any data post processing as well as final UTF8 + * validation checks for text messages. + * + * @return A code indicating errors, if any + */ + lib::error_code finalize_message() { + std::string & out = m_current_msg->msg_ptr->get_raw_payload(); + + // if the frame is compressed, append the compression + // trailer and flush the compression buffer. + if (m_permessage_deflate.is_enabled() + && m_current_msg->msg_ptr->get_compressed()) + { + uint8_t trailer[4] = {0x00, 0x00, 0xff, 0xff}; + + // Decompress current buffer into the message buffer + lib::error_code ec; + ec = m_permessage_deflate.decompress(trailer,4,out); + if (ec) { + return ec; + } + } + + // ensure that text messages end on a valid UTF8 code point + if (frame::get_opcode(m_basic_header) == frame::opcode::TEXT) { + if (!m_current_msg->validator.complete()) { + return make_error_code(error::invalid_utf8); + } + } + + m_state = READY; + + return lib::error_code(); + } + + void reset_headers() { + m_state = HEADER_BASIC; + m_bytes_needed = frame::BASIC_HEADER_LENGTH; + + m_basic_header.b0 = 0x00; + m_basic_header.b1 = 0x00; + + std::fill_n( + m_extended_header.bytes, + frame::MAX_EXTENDED_HEADER_LENGTH, + 0x00 + ); + } + + /// Test whether or not the processor has a message ready + bool ready() const { + return (m_state == READY); + } + + message_ptr get_message() { + if (!ready()) { + return message_ptr(); + } + message_ptr ret = m_current_msg->msg_ptr; + m_current_msg->msg_ptr.reset(); + + if (frame::opcode::is_control(ret->get_opcode())) { + m_control_msg.msg_ptr.reset(); + } else { + m_data_msg.msg_ptr.reset(); + } + + this->reset_headers(); + + return ret; + } + + /// Test whether or not the processor is in a fatal error state. + bool get_error() const { + return m_state == FATAL_ERROR; + } + + size_t get_bytes_needed() const { + return m_bytes_needed; + } + + /// Prepare a user data message for writing + /** + * Performs validation, masking, compression, etc. will return an error if + * there was an error, otherwise msg will be ready to be written + * + * TODO: tests + * + * @param in An unprepared message to prepare + * @param out A message to be overwritten with the prepared message + * @return error code + */ + virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) + { + if (!in || !out) { + return make_error_code(error::invalid_arguments); + } + + frame::opcode::value op = in->get_opcode(); + + // validate opcode: only regular data frames + if (frame::opcode::is_control(op)) { + return make_error_code(error::invalid_opcode); + } + + std::string& i = in->get_raw_payload(); + std::string& o = out->get_raw_payload(); + + // validate payload utf8 + if (op == frame::opcode::TEXT && !utf8_validator::validate(i)) { + return make_error_code(error::invalid_payload); + } + + frame::masking_key_type key; + bool masked = !base::m_server; + bool compressed = m_permessage_deflate.is_enabled() + && in->get_compressed(); + bool fin = in->get_fin(); + + if (masked) { + // Generate masking key. + key.i = m_rng(); + } else { + key.i = 0; + } + + // prepare payload + if (compressed) { + // compress and store in o after header. + m_permessage_deflate.compress(i,o); + + if (o.size() < 4) { + return make_error_code(error::general); + } + + // Strip trailing 4 0x00 0x00 0xff 0xff bytes before writing to the + // wire + o.resize(o.size()-4); + + // mask in place if necessary + if (masked) { + this->masked_copy(o,o,key); + } + } else { + // no compression, just copy data into the output buffer + o.resize(i.size()); + + // if we are masked, have the masking function write to the output + // buffer directly to avoid another copy. If not masked, copy + // directly without masking. + if (masked) { + this->masked_copy(i,o,key); + } else { + std::copy(i.begin(),i.end(),o.begin()); + } + } + + // generate header + frame::basic_header h(op,o.size(),fin,masked,compressed); + + if (masked) { + frame::extended_header e(o.size(),key.i); + out->set_header(frame::prepare_header(h,e)); + } else { + frame::extended_header e(o.size()); + out->set_header(frame::prepare_header(h,e)); + } + + out->set_prepared(true); + out->set_opcode(op); + + return lib::error_code(); + } + + /// Get URI + lib::error_code prepare_ping(std::string const & in, message_ptr out) const { + return this->prepare_control(frame::opcode::PING,in,out); + } + + lib::error_code prepare_pong(std::string const & in, message_ptr out) const { + return this->prepare_control(frame::opcode::PONG,in,out); + } + + virtual lib::error_code prepare_close(close::status::value code, + std::string const & reason, message_ptr out) const + { + if (close::status::reserved(code)) { + return make_error_code(error::reserved_close_code); + } + + if (close::status::invalid(code) && code != close::status::no_status) { + return make_error_code(error::invalid_close_code); + } + + if (code == close::status::no_status && reason.size() > 0) { + return make_error_code(error::reason_requires_code); + } + + if (reason.size() > frame:: limits::payload_size_basic-2) { + return make_error_code(error::control_too_big); + } + + std::string payload; + + if (code != close::status::no_status) { + close::code_converter val; + val.i = htons(code); + + payload.resize(reason.size()+2); + + payload[0] = val.c[0]; + payload[1] = val.c[1]; + + std::copy(reason.begin(),reason.end(),payload.begin()+2); + } + + return this->prepare_control(frame::opcode::CLOSE,payload,out); + } +protected: + /// Convert a client handshake key into a server response key in place + lib::error_code process_handshake_key(std::string & key) const { + key.append(constants::handshake_guid); + + unsigned char message_digest[20]; + sha1::calc(key.c_str(),key.length(),message_digest); + key = base64_encode(message_digest,20); + + return lib::error_code(); + } + + /// Reads bytes from buf into m_basic_header + size_t copy_basic_header_bytes(uint8_t const * buf, size_t len) { + if (len == 0 || m_bytes_needed == 0) { + return 0; + } + + if (len > 1) { + // have at least two bytes + if (m_bytes_needed == 2) { + m_basic_header.b0 = buf[0]; + m_basic_header.b1 = buf[1]; + m_bytes_needed -= 2; + return 2; + } else { + m_basic_header.b1 = buf[0]; + m_bytes_needed--; + return 1; + } + } else { + // have exactly one byte + if (m_bytes_needed == 2) { + m_basic_header.b0 = buf[0]; + m_bytes_needed--; + return 1; + } else { + m_basic_header.b1 = buf[0]; + m_bytes_needed--; + return 1; + } + } + } + + /// Reads bytes from buf into m_extended_header + size_t copy_extended_header_bytes(uint8_t const * buf, size_t len) { + size_t bytes_to_read = (std::min)(m_bytes_needed,len); + + std::copy(buf,buf+bytes_to_read,m_extended_header.bytes+m_cursor); + m_cursor += bytes_to_read; + m_bytes_needed -= bytes_to_read; + + return bytes_to_read; + } + + /// Reads bytes from buf into message payload + /** + * This function performs unmasking and uncompression, validates the + * decoded bytes, and writes them to the appropriate message buffer. + * + * This member function will use the input buffer as stratch space for its + * work. The raw input bytes will not be preserved. This applies only to the + * bytes actually needed. At most min(m_bytes_needed,len) will be processed. + * + * @param buf Input/working buffer + * @param len Length of buf + * @return Number of bytes processed or zero in case of an error + */ + size_t process_payload_bytes(uint8_t * buf, size_t len, lib::error_code& ec) + { + // unmask if masked + if (frame::get_masked(m_basic_header)) { + m_current_msg->prepared_key = frame::byte_mask_circ( + buf, len, m_current_msg->prepared_key); + // TODO: SIMD masking + } + + std::string & out = m_current_msg->msg_ptr->get_raw_payload(); + size_t offset = out.size(); + + // decompress message if needed. + if (m_permessage_deflate.is_enabled() + && m_current_msg->msg_ptr->get_compressed()) + { + // Decompress current buffer into the message buffer + ec = m_permessage_deflate.decompress(buf,len,out); + if (ec) { + return 0; + } + } else { + // No compression, straight copy + out.append(reinterpret_cast(buf),len); + } + + // validate unmasked, decompressed values + if (m_current_msg->msg_ptr->get_opcode() == frame::opcode::TEXT) { + if (!m_current_msg->validator.decode(out.begin()+offset,out.end())) { + ec = make_error_code(error::invalid_utf8); + return 0; + } + } + + m_bytes_needed -= len; + + return len; + } + + /// Validate an incoming basic header + /** + * Validates an incoming hybi13 basic header. + * + * @param h The basic header to validate + * @param is_server Whether or not the endpoint that received this frame + * is a server. + * @param new_msg Whether or not this is the first frame of the message + * @return 0 on success or a non-zero error code on failure + */ + lib::error_code validate_incoming_basic_header(frame::basic_header const & h, + bool is_server, bool new_msg) const + { + frame::opcode::value op = frame::get_opcode(h); + + // Check control frame size limit + if (frame::opcode::is_control(op) && + frame::get_basic_size(h) > frame::limits::payload_size_basic) + { + return make_error_code(error::control_too_big); + } + + // Check that RSV bits are clear + // The only RSV bits allowed are rsv1 if the permessage_compress + // extension is enabled for this connection and the message is not + // a control message. + // + // TODO: unit tests for this + if (frame::get_rsv1(h) && (!m_permessage_deflate.is_enabled() + || frame::opcode::is_control(op))) + { + return make_error_code(error::invalid_rsv_bit); + } + + if (frame::get_rsv2(h) || frame::get_rsv3(h)) { + return make_error_code(error::invalid_rsv_bit); + } + + // Check for reserved opcodes + if (frame::opcode::reserved(op)) { + return make_error_code(error::invalid_opcode); + } + + // Check for invalid opcodes + // TODO: unit tests for this? + if (frame::opcode::invalid(op)) { + return make_error_code(error::invalid_opcode); + } + + // Check for fragmented control message + if (frame::opcode::is_control(op) && !frame::get_fin(h)) { + return make_error_code(error::fragmented_control); + } + + // Check for continuation without an active message + if (new_msg && op == frame::opcode::CONTINUATION) { + return make_error_code(error::invalid_continuation); + } + + // Check for new data frame when expecting continuation + if (!new_msg && !frame::opcode::is_control(op) && + op != frame::opcode::CONTINUATION) + { + return make_error_code(error::invalid_continuation); + } + + // Servers should reject any unmasked frames from clients. + // Clients should reject any masked frames from servers. + if (is_server && !frame::get_masked(h)) { + return make_error_code(error::masking_required); + } else if (!is_server && frame::get_masked(h)) { + return make_error_code(error::masking_forbidden); + } + + return lib::error_code(); + } + + /// Validate an incoming extended header + /** + * Validates an incoming hybi13 full header. + * + * @todo unit test for the >32 bit frames on 32 bit systems case + * + * @param h The basic header to validate + * @param e The extended header to validate + * @return An error_code, non-zero values indicate why the validation + * failed + */ + lib::error_code validate_incoming_extended_header(frame::basic_header h, + frame::extended_header e) const + { + uint8_t basic_size = frame::get_basic_size(h); + uint64_t payload_size = frame::get_payload_size(h,e); + + // Check for non-minimally encoded payloads + if (basic_size == frame::payload_size_code_16bit && + payload_size <= frame::limits::payload_size_basic) + { + return make_error_code(error::non_minimal_encoding); + } + + if (basic_size == frame::payload_size_code_64bit && + payload_size <= frame::limits::payload_size_extended) + { + return make_error_code(error::non_minimal_encoding); + } + + // Check for >32bit frames on 32 bit systems + if (sizeof(size_t) == 4 && (payload_size >> 32)) { + return make_error_code(error::requires_64bit); + } + + return lib::error_code(); + } + + /// Copy and mask/unmask in one operation + /** + * Reads input from one string and writes unmasked output to another. + * + * @param [in] i The input string. + * @param [out] o The output string. + * @param [in] key The masking key to use for masking/unmasking + */ + void masked_copy (std::string const & i, std::string & o, + frame::masking_key_type key) const + { + frame::byte_mask(i.begin(),i.end(),o.begin(),key); + // TODO: SIMD masking + } + + /// Generic prepare control frame with opcode and payload. + /** + * Internal control frame building method. Handles validation, masking, etc + * + * @param op The control opcode to use + * @param payload The payload to use + * @param out The message buffer to store the prepared frame in + * @return Status code, zero on success, non-zero on error + */ + lib::error_code prepare_control(frame::opcode::value op, + std::string const & payload, message_ptr out) const + { + if (!out) { + return make_error_code(error::invalid_arguments); + } + + if (!frame::opcode::is_control(op)) { + return make_error_code(error::invalid_opcode); + } + + if (payload.size() > frame::limits::payload_size_basic) { + return make_error_code(error::control_too_big); + } + + frame::masking_key_type key; + bool masked = !base::m_server; + + frame::basic_header h(op,payload.size(),true,masked); + + std::string & o = out->get_raw_payload(); + o.resize(payload.size()); + + if (masked) { + // Generate masking key. + key.i = m_rng(); + + frame::extended_header e(payload.size(),key.i); + out->set_header(frame::prepare_header(h,e)); + this->masked_copy(payload,o,key); + } else { + frame::extended_header e(payload.size()); + out->set_header(frame::prepare_header(h,e)); + std::copy(payload.begin(),payload.end(),o.begin()); + } + + out->set_opcode(op); + out->set_prepared(true); + + return lib::error_code(); + } + + enum state { + HEADER_BASIC = 0, + HEADER_EXTENDED = 1, + EXTENSION = 2, + APPLICATION = 3, + READY = 4, + FATAL_ERROR = 5 + }; + + /// This data structure holds data related to processing a message, such as + /// the buffer it is being written to, its masking key, its UTF8 validation + /// state, and sometimes its compression state. + struct msg_metadata { + msg_metadata() {} + msg_metadata(message_ptr m, size_t p) : msg_ptr(m),prepared_key(p) {} + msg_metadata(message_ptr m, frame::masking_key_type p) + : msg_ptr(m) + , prepared_key(prepare_masking_key(p)) {} + + message_ptr msg_ptr; // pointer to the message data buffer + size_t prepared_key; // prepared masking key + utf8_validator::validator validator; // utf8 validation state + }; + + // Basic header of the frame being read + frame::basic_header m_basic_header; + + // Pointer to a manager that can create message buffers for us. + msg_manager_ptr m_msg_manager; + + // Number of bytes needed to complete the current operation + size_t m_bytes_needed; + + // Number of extended header bytes read + size_t m_cursor; + + // Metadata for the current data msg + msg_metadata m_data_msg; + // Metadata for the current control msg + msg_metadata m_control_msg; + + // Pointer to the metadata associated with the frame being read + msg_metadata * m_current_msg; + + // Extended header of current frame + frame::extended_header m_extended_header; + + rng_type & m_rng; + + // Overall state of the processor + state m_state; + + // Extensions + permessage_deflate_type m_permessage_deflate; +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI13_HPP diff --git a/subprojects/websocketpp/websocketpp/processors/processor.hpp b/subprojects/websocketpp/websocketpp/processors/processor.hpp new file mode 100644 index 000000000..5131cc45e --- /dev/null +++ b/subprojects/websocketpp/websocketpp/processors/processor.hpp @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HPP +#define WEBSOCKETPP_PROCESSOR_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { +/// Processors encapsulate the protocol rules specific to each WebSocket version +/** + * The processors namespace includes a number of free functions that operate on + * various WebSocket related data structures and perform processing that is not + * related to specific versions of the protocol. + * + * It also includes the abstract interface for the protocol specific processing + * engines. These engines wrap all of the logic necessary for parsing and + * validating WebSocket handshakes and messages of specific protocol version + * and set of allowed extensions. + * + * An instance of a processor represents the state of a single WebSocket + * connection of the associated version. One processor instance is needed per + * logical WebSocket connection. + */ +namespace processor { + +/// Determine whether or not a generic HTTP request is a WebSocket handshake +/** + * @param r The HTTP request to read. + * + * @return True if the request is a WebSocket handshake, false otherwise + */ +template +bool is_websocket_handshake(request_type& r) { + using utility::ci_find_substr; + + std::string const & upgrade_header = r.get_header("Upgrade"); + + if (ci_find_substr(upgrade_header, constants::upgrade_token, + sizeof(constants::upgrade_token)-1) == upgrade_header.end()) + { + return false; + } + + std::string const & con_header = r.get_header("Connection"); + + if (ci_find_substr(con_header, constants::connection_token, + sizeof(constants::connection_token)-1) == con_header.end()) + { + return false; + } + + return true; +} + +/// Extract the version from a WebSocket handshake request +/** + * A blank version header indicates a spec before versions were introduced. + * The only such versions in shipping products are Hixie Draft 75 and Hixie + * Draft 76. Draft 75 is present in Chrome 4-5 and Safari 5.0.0, Draft 76 (also + * known as hybi 00 is present in Chrome 6-13 and Safari 5.0.1+. As + * differentiating between these two sets of browsers is very difficult and + * Safari 5.0.1+ accounts for the vast majority of cases in the wild this + * function assumes that all handshakes without a valid version header are + * Hybi 00. + * + * @param r The WebSocket handshake request to read. + * + * @return The WebSocket handshake version or -1 if there was an extraction + * error. + */ +template +int get_websocket_version(request_type& r) { + if (!r.ready()) { + return -2; + } + + if (r.get_header("Sec-WebSocket-Version").empty()) { + return 0; + } + + int version; + std::istringstream ss(r.get_header("Sec-WebSocket-Version")); + + if ((ss >> version).fail()) { + return -1; + } + + return version; +} + +/// Extract a URI ptr from the host header of the request +/** + * @param request The request to extract the Host header from. + * + * @param scheme The scheme under which this request was received (ws, wss, + * http, https, etc) + * + * @return A uri_pointer that encodes the value of the host header. + */ +template +uri_ptr get_uri_from_host(request_type & request, std::string scheme) { + std::string h = request.get_header("Host"); + + size_t last_colon = h.rfind(":"); + size_t last_sbrace = h.rfind("]"); + + // no : = hostname with no port + // last : before ] = ipv6 literal with no port + // : with no ] = hostname with port + // : after ] = ipv6 literal with port + if (last_colon == std::string::npos || + (last_sbrace != std::string::npos && last_sbrace > last_colon)) + { + return lib::make_shared(scheme, h, request.get_uri()); + } else { + return lib::make_shared(scheme, + h.substr(0,last_colon), + h.substr(last_colon+1), + request.get_uri()); + } +} + +/// WebSocket protocol processor abstract base class +template +class processor { +public: + typedef processor type; + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + typedef typename config::message_type::ptr message_ptr; + typedef std::pair err_str_pair; + + explicit processor(bool secure, bool p_is_server) + : m_secure(secure) + , m_server(p_is_server) + , m_max_message_size(config::max_message_size) + {} + + virtual ~processor() {} + + /// Get the protocol version of this processor + virtual int get_version() const = 0; + + /// Get maximum message size + /** + * Get maximum message size. Maximum message size determines the point at which the + * processor will fail a connection with the message_too_big protocol error. + * + * The default is retrieved from the max_message_size value from the template config + * + * @since 0.3.0 + */ + size_t get_max_message_size() const { + return m_max_message_size; + } + + /// Set maximum message size + /** + * Set maximum message size. Maximum message size determines the point at which the + * processor will fail a connection with the message_too_big protocol error. + * + * The default is retrieved from the max_message_size value from the template config + * + * @since 0.3.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_message_size(size_t new_value) { + m_max_message_size = new_value; + } + + /// Returns whether or not the permessage_compress extension is implemented + /** + * Compile time flag that indicates whether this processor has implemented + * the permessage_compress extension. By default this is false. + */ + virtual bool has_permessage_compress() const { + return false; + } + + /// Initializes extensions based on the Sec-WebSocket-Extensions header + /** + * Reads the Sec-WebSocket-Extensions header and determines if any of the + * requested extensions are supported by this processor. If they are their + * settings data is initialized and an extension string to send to the + * is returned. + * + * @param request The request or response headers to look at. + */ + virtual err_str_pair negotiate_extensions(request_type const &) { + return err_str_pair(); + } + + /// Initializes extensions based on the Sec-WebSocket-Extensions header + /** + * Reads the Sec-WebSocket-Extensions header and determines if any of the + * requested extensions were accepted by the server. If they are their + * settings data is initialized. If they are not a list of required + * extensions (if any) is returned. This list may be sent back to the server + * as a part of the 1010/Extension required close code. + * + * @param response The request or response headers to look at. + */ + virtual err_str_pair negotiate_extensions(response_type const &) { + return err_str_pair(); + } + + /// validate a WebSocket handshake request for this version + /** + * @param request The WebSocket handshake request to validate. + * is_websocket_handshake(request) must be true and + * get_websocket_version(request) must equal this->get_version(). + * + * @return A status code, 0 on success, non-zero for specific sorts of + * failure + */ + virtual lib::error_code validate_handshake(request_type const & request) const = 0; + + /// Calculate the appropriate response for this websocket request + /** + * @param req The request to process + * + * @param subprotocol The subprotocol in use + * + * @param res The response to store the processed response in + * + * @return An error code, 0 on success, non-zero for other errors + */ + virtual lib::error_code process_handshake(request_type const & req, + std::string const & subprotocol, response_type& res) const = 0; + + /// Fill in an HTTP request for an outgoing connection handshake + /** + * @param req The request to process. + * + * @return An error code, 0 on success, non-zero for other errors + */ + virtual lib::error_code client_handshake_request(request_type & req, + uri_ptr uri, std::vector const & subprotocols) const = 0; + + /// Validate the server's response to an outgoing handshake request + /** + * @param req The original request sent + * @param res The reponse to generate + * @return An error code, 0 on success, non-zero for other errors + */ + virtual lib::error_code validate_server_handshake_response(request_type + const & req, response_type & res) const = 0; + + /// Given a completed response, get the raw bytes to put on the wire + virtual std::string get_raw(response_type const & request) const = 0; + + /// Return the value of the header containing the CORS origin. + virtual std::string const & get_origin(request_type const & request) const = 0; + + /// Extracts requested subprotocols from a handshake request + /** + * Extracts a list of all subprotocols that the client has requested in the + * given opening handshake request. + * + * @param [in] req The request to extract from + * @param [out] subprotocol_list A reference to a vector of strings to store + * the results in. + */ + virtual lib::error_code extract_subprotocols(const request_type & req, + std::vector & subprotocol_list) = 0; + + /// Extracts client uri from a handshake request + virtual uri_ptr get_uri(request_type const & request) const = 0; + + /// process new websocket connection bytes + /** + * WebSocket connections are a continous stream of bytes that must be + * interpreted by a protocol processor into discrete frames. + * + * @param buf Buffer from which bytes should be read. + * @param len Length of buffer + * @param ec Reference to an error code to return any errors in + * @return Number of bytes processed + */ + virtual size_t consume(uint8_t *buf, size_t len, lib::error_code & ec) = 0; + + /// Checks if there is a message ready + /** + * Checks if the most recent consume operation processed enough bytes to + * complete a new WebSocket message. The message can be retrieved by calling + * get_message() which will reset the internal state to not-ready and allow + * consume to read more bytes. + * + * @return Whether or not a message is ready. + */ + virtual bool ready() const = 0; + + /// Retrieves the most recently processed message + /** + * Retrieves a shared pointer to the recently completed message if there is + * one. If ready() returns true then there is a message available. + * Retrieving the message with get_message will reset the state of ready. + * As such, each new message may be retrieved only once. Calling get_message + * when there is no message available will result in a null pointer being + * returned. + * + * @return A pointer to the most recently processed message or a null shared + * pointer. + */ + virtual message_ptr get_message() = 0; + + /// Tests whether the processor is in a fatal error state + virtual bool get_error() const = 0; + + /// Retrieves the number of bytes presently needed by the processor + /// This value may be used as a hint to the transport layer as to how many + /// bytes to wait for before running consume again. + virtual size_t get_bytes_needed() const { + return 1; + } + + /// Prepare a data message for writing + /** + * Performs validation, masking, compression, etc. will return an error if + * there was an error, otherwise msg will be ready to be written + */ + virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0; + + /// Prepare a ping frame + /** + * Ping preparation is entirely state free. There is no payload validation + * other than length. Payload need not be UTF-8. + * + * @param in The string to use for the ping payload + * @param out The message buffer to prepare the ping in. + * @return Status code, zero on success, non-zero on failure + */ + virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const + = 0; + + /// Prepare a pong frame + /** + * Pong preparation is entirely state free. There is no payload validation + * other than length. Payload need not be UTF-8. + * + * @param in The string to use for the pong payload + * @param out The message buffer to prepare the pong in. + * @return Status code, zero on success, non-zero on failure + */ + virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const + = 0; + + /// Prepare a close frame + /** + * Close preparation is entirely state free. The code and reason are both + * subject to validation. Reason must be valid UTF-8. Code must be a valid + * un-reserved WebSocket close code. Use close::status::no_status to + * indicate no code. If no code is supplied a reason may not be specified. + * + * @param code The close code to send + * @param reason The reason string to send + * @param out The message buffer to prepare the fame in + * @return Status code, zero on success, non-zero on failure + */ + virtual lib::error_code prepare_close(close::status::value code, + std::string const & reason, message_ptr out) const = 0; +protected: + bool const m_secure; + bool const m_server; + size_t m_max_message_size; +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HPP diff --git a/subprojects/websocketpp/websocketpp/random/none.hpp b/subprojects/websocketpp/websocketpp/random/none.hpp new file mode 100644 index 000000000..2163e6f4a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/random/none.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_RANDOM_NONE_HPP +#define WEBSOCKETPP_RANDOM_NONE_HPP + +namespace websocketpp { +/// Random number generation policies +namespace random { +/// Stub RNG policy that always returns 0 +namespace none { + +/// Thread safe stub "random" integer generator. +/** + * This template class provides a random integer stub. The interface mimics the + * WebSocket++ RNG generator classes but the generater function always returns + * zero. This can be used to stub out the RNG for unit and performance testing. + * + * Call operator() to generate the next number + */ +template +class int_generator { + public: + int_generator() {} + + /// advances the engine's state and returns the generated value + int_type operator()() { + return 0; + } +}; + +} // namespace none +} // namespace random +} // namespace websocketpp + +#endif //WEBSOCKETPP_RANDOM_NONE_HPP diff --git a/subprojects/websocketpp/websocketpp/random/random_device.hpp b/subprojects/websocketpp/websocketpp/random/random_device.hpp new file mode 100644 index 000000000..91e7dd162 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/random/random_device.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP +#define WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP + +#include + +namespace websocketpp { +namespace random { +/// RNG policy based on std::random_device or boost::random_device +namespace random_device { + +/// Thread safe non-deterministic random integer generator. +/** + * This template class provides thread safe non-deterministic random integer + * generation. Numbers are produced in a uniformly distributed range from the + * smallest to largest value that int_type can store. + * + * Thread-safety is provided via locking based on the concurrency template + * parameter. + * + * Non-deterministic RNG is provided via websocketpp::lib which uses either + * C++11 or Boost 1.47+'s random_device class. + * + * Call operator() to generate the next number + */ +template +class int_generator { + public: + typedef typename concurrency::scoped_lock_type scoped_lock_type; + typedef typename concurrency::mutex_type mutex_type; + + /// constructor + //mac TODO: figure out if signed types present a range problem + int_generator() {} + + /// advances the engine's state and returns the generated value + int_type operator()() { + scoped_lock_type guard(m_lock); + return m_dis(m_rng); + } + private: + + + lib::random_device m_rng; + lib::uniform_int_distribution m_dis; + + mutex_type m_lock; +}; + +} // namespace random_device +} // namespace random +} // namespace websocketpp + +#endif //WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP diff --git a/subprojects/websocketpp/websocketpp/roles/client_endpoint.hpp b/subprojects/websocketpp/websocketpp/roles/client_endpoint.hpp new file mode 100644 index 000000000..4d0c433b0 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/roles/client_endpoint.hpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CLIENT_ENDPOINT_HPP +#define WEBSOCKETPP_CLIENT_ENDPOINT_HPP + +#include +#include + +#include + +#include + +#include + +namespace websocketpp { + +/// Client endpoint role based on the given config +/** + * + */ +template +class client : public endpoint,config> { +public: + /// Type of this endpoint + typedef client type; + + /// Type of the endpoint concurrency component + typedef typename config::concurrency_type concurrency_type; + /// Type of the endpoint transport component + typedef typename config::transport_type transport_type; + + /// Type of the connections this server will create + typedef connection connection_type; + /// Type of a shared pointer to the connections this server will create + typedef typename connection_type::ptr connection_ptr; + + /// Type of the connection transport component + typedef typename transport_type::transport_con_type transport_con_type; + /// Type of a shared pointer to the connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of the endpoint component of this server + typedef endpoint endpoint_type; + + friend class connection; + + explicit client() : endpoint_type(false) + { + endpoint_type::m_alog->write(log::alevel::devel, "client constructor"); + } + + /// Get a new connection + /** + * Creates and returns a pointer to a new connection to the given URI + * suitable for passing to connect(connection_ptr). This method allows + * applying connection specific settings before performing the opening + * handshake. + * + * @param [in] location URI to open the connection to as a uri_ptr + * @param [out] ec An status code indicating failure reasons, if any + * + * @return A connection_ptr to the new connection + */ + connection_ptr get_connection(uri_ptr location, lib::error_code & ec) { + if (location->get_secure() && !transport_type::is_secure()) { + ec = error::make_error_code(error::endpoint_not_secure); + return connection_ptr(); + } + + connection_ptr con = endpoint_type::create_connection(); + + if (!con) { + ec = error::make_error_code(error::con_creation_failed); + return con; + } + + con->set_uri(location); + + ec = lib::error_code(); + return con; + } + + /// Get a new connection (string version) + /** + * Creates and returns a pointer to a new connection to the given URI + * suitable for passing to connect(connection_ptr). This overload allows + * default construction of the uri_ptr from a standard string. + * + * @param [in] u URI to open the connection to as a string + * @param [out] ec An status code indicating failure reasons, if any + * + * @return A connection_ptr to the new connection + */ + connection_ptr get_connection(std::string const & u, lib::error_code & ec) { + uri_ptr location = lib::make_shared(u); + + if (!location->get_valid()) { + ec = error::make_error_code(error::invalid_uri); + return connection_ptr(); + } + + return get_connection(location, ec); + } + + /// Begin the connection process for the given connection + /** + * Initiates the opening connection handshake for connection con. Exact + * behavior depends on the underlying transport policy. + * + * @param con The connection to connect + * + * @return The pointer to the connection originally passed in. + */ + connection_ptr connect(connection_ptr con) { + // Ask transport to perform a connection + transport_type::async_connect( + lib::static_pointer_cast(con), + con->get_uri(), + lib::bind( + &type::handle_connect, + this, + con, + lib::placeholders::_1 + ) + ); + + return con; + } +private: + // handle_connect + void handle_connect(connection_ptr con, lib::error_code const & ec) { + if (ec) { + con->terminate(ec); + + endpoint_type::m_elog->write(log::elevel::rerror, + "handle_connect error: "+ec.message()); + } else { + endpoint_type::m_alog->write(log::alevel::connect, + "Successful connection"); + + con->start(); + } + } +}; + +} // namespace websocketpp + +#endif //WEBSOCKETPP_CLIENT_ENDPOINT_HPP diff --git a/subprojects/websocketpp/websocketpp/roles/server_endpoint.hpp b/subprojects/websocketpp/websocketpp/roles/server_endpoint.hpp new file mode 100644 index 000000000..9cc652f75 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/roles/server_endpoint.hpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_SERVER_ENDPOINT_HPP +#define WEBSOCKETPP_SERVER_ENDPOINT_HPP + +#include + +#include + +#include + +namespace websocketpp { + +/// Server endpoint role based on the given config +/** + * + */ +template +class server : public endpoint,config> { +public: + /// Type of this endpoint + typedef server type; + + /// Type of the endpoint concurrency component + typedef typename config::concurrency_type concurrency_type; + /// Type of the endpoint transport component + typedef typename config::transport_type transport_type; + + /// Type of the connections this server will create + typedef connection connection_type; + /// Type of a shared pointer to the connections this server will create + typedef typename connection_type::ptr connection_ptr; + + /// Type of the connection transport component + typedef typename transport_type::transport_con_type transport_con_type; + /// Type of a shared pointer to the connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of the endpoint component of this server + typedef endpoint endpoint_type; + + friend class connection; + + explicit server() : endpoint_type(true) + { + endpoint_type::m_alog->write(log::alevel::devel, "server constructor"); + } + + /// Destructor + ~server() {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy constructor because endpoints are not copyable + server(server &) = delete; + + // no copy assignment operator because endpoints are not copyable + server & operator=(server const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + server(server && o) : endpoint,config>(std::move(o)) {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + server & operator=(server &&) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + /// Create and initialize a new connection + /** + * The connection will be initialized and ready to begin. Call its start() + * method to begin the processing loop. + * + * Note: The connection must either be started or terminated using + * connection::terminate in order to avoid memory leaks. + * + * @return A pointer to the new connection. + */ + connection_ptr get_connection() { + return endpoint_type::create_connection(); + } + + /// Starts the server's async connection acceptance loop (exception free) + /** + * Initiates the server connection acceptance loop. Must be called after + * listen. This method will have no effect until the underlying io_service + * starts running. It may be called after the io_service is already running. + * + * Refer to documentation for the transport policy you are using for + * instructions on how to stop this acceptance loop. + * + * @param [out] ec A status code indicating an error, if any. + */ + void start_accept(lib::error_code & ec) { + if (!transport_type::is_listening()) { + ec = error::make_error_code(error::async_accept_not_listening); + return; + } + + ec = lib::error_code(); + connection_ptr con = get_connection(); + + if (!con) { + ec = error::make_error_code(error::con_creation_failed); + return; + } + + transport_type::async_accept( + lib::static_pointer_cast(con), + lib::bind(&type::handle_accept,this,con,lib::placeholders::_1), + ec + ); + + if (ec && con) { + // If the connection was constructed but the accept failed, + // terminate the connection to prevent memory leaks + con->terminate(lib::error_code()); + } + } + + /// Starts the server's async connection acceptance loop + /** + * Initiates the server connection acceptance loop. Must be called after + * listen. This method will have no effect until the underlying io_service + * starts running. It may be called after the io_service is already running. + * + * Refer to documentation for the transport policy you are using for + * instructions on how to stop this acceptance loop. + */ + void start_accept() { + lib::error_code ec; + start_accept(ec); + if (ec) { + throw exception(ec); + } + } + + /// Handler callback for start_accept + void handle_accept(connection_ptr con, lib::error_code const & ec) { + if (ec) { + con->terminate(ec); + + if (ec == error::operation_canceled) { + endpoint_type::m_elog->write(log::elevel::info, + "handle_accept error: "+ec.message()); + } else { + endpoint_type::m_elog->write(log::elevel::rerror, + "handle_accept error: "+ec.message()); + } + } else { + con->start(); + } + + lib::error_code start_ec; + start_accept(start_ec); + if (start_ec == error::async_accept_not_listening) { + endpoint_type::m_elog->write(log::elevel::info, + "Stopping acceptance of new connections because the underlying transport is no longer listening."); + } else if (start_ec) { + endpoint_type::m_elog->write(log::elevel::rerror, + "Restarting async_accept loop failed: "+ec.message()); + } + } +}; + +} // namespace websocketpp + +#endif //WEBSOCKETPP_SERVER_ENDPOINT_HPP diff --git a/subprojects/websocketpp/websocketpp/server.hpp b/subprojects/websocketpp/websocketpp/server.hpp new file mode 100644 index 000000000..342fa8c54 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/server.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_SERVER_HPP +#define WEBSOCKETPP_SERVER_HPP + +#include + +#endif //WEBSOCKETPP_SERVER_HPP diff --git a/subprojects/websocketpp/websocketpp/sha1/sha1.hpp b/subprojects/websocketpp/websocketpp/sha1/sha1.hpp new file mode 100644 index 000000000..6b48d9578 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/sha1/sha1.hpp @@ -0,0 +1,189 @@ +/* +***** +sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the smallsha1 +library (http://code.google.com/p/smallsha1/) into a single header suitable for +use as a header only library. This conversion was done by Peter Thorson +(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed +under the same license as the original, which is listed below. +***** + + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SHA1_DEFINED +#define SHA1_DEFINED + +namespace websocketpp { +namespace sha1 { + +namespace { // local + +// Rotate an integer value to left. +inline unsigned int rol(unsigned int value, unsigned int steps) { + return ((value << steps) | (value >> (32 - steps))); +} + +// Sets the first 16 integers in the buffert to zero. +// Used for clearing the W buffert. +inline void clearWBuffert(unsigned int * buffert) +{ + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } +} + +inline void innerHash(unsigned int * result, unsigned int * w) +{ + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + + #define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) + { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + + #undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; +} + +} // namespace + +/// Calculate a SHA1 hash +/** + * @param src points to any kind of data to be hashed. + * @param bytelength the number of bytes to hash from the src pointer. + * @param hash should point to a buffer of at least 20 bytes of size for storing + * the sha1 result in. + */ +inline void calc(void const * src, size_t bytelength, unsigned char * hash) { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, + 0x10325476, 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + unsigned char const * sarray = (unsigned char const *) src; + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + + size_t endCurrentBlock; + size_t currentBlock = 0; + + if (bytelength >= 64) { + size_t const endOfFullBlocks = bytelength - 64; + + while (currentBlock <= endOfFullBlocks) { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on + // little endian. + w[roundPos++] = (unsigned int) sarray[currentBlock + 3] + | (((unsigned int) sarray[currentBlock + 2]) << 8) + | (((unsigned int) sarray[currentBlock + 1]) << 16) + | (((unsigned int) sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + size_t lastBlockBytes = 0; + for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) { + w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); + } + + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct + // order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; + } +} + +} // namespace sha1 +} // namespace websocketpp + +#endif // SHA1_DEFINED diff --git a/subprojects/websocketpp/websocketpp/transport/asio/base.hpp b/subprojects/websocketpp/websocketpp/transport/asio/base.hpp new file mode 100644 index 000000000..b945fe11f --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/asio/base.hpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP + +#include +#include +#include +#include +#include + +#include + +namespace websocketpp { +namespace transport { +/// Transport policy that uses asio +/** + * This policy uses a single asio io_service to provide transport + * services to a WebSocket++ endpoint. + */ +namespace asio { + +// Class to manage the memory to be used for handler-based custom allocation. +// It contains a single block of memory which may be returned for allocation +// requests. If the memory is in use when an allocation request is made, the +// allocator delegates allocation to the global heap. +class handler_allocator { +public: + static const size_t size = 1024; + + handler_allocator() : m_in_use(false) {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + handler_allocator(handler_allocator const & cpy) = delete; + handler_allocator & operator =(handler_allocator const &) = delete; +#endif + + void * allocate(std::size_t memsize) { + if (!m_in_use && memsize < size) { + m_in_use = true; + return static_cast(&m_storage); + } else { + return ::operator new(memsize); + } + } + + void deallocate(void * pointer) { + if (pointer == &m_storage) { + m_in_use = false; + } else { + ::operator delete(pointer); + } + } + +private: + // Storage space used for handler-based custom memory allocation. + lib::aligned_storage::type m_storage; + + // Whether the handler-based custom allocation storage has been used. + bool m_in_use; +}; + +// Wrapper class template for handler objects to allow handler memory +// allocation to be customised. Calls to operator() are forwarded to the +// encapsulated handler. +template +class custom_alloc_handler { +public: + custom_alloc_handler(handler_allocator& a, Handler h) + : allocator_(a), + handler_(h) + {} + + template + void operator()(Arg1 arg1) { + handler_(arg1); + } + + template + void operator()(Arg1 arg1, Arg2 arg2) { + handler_(arg1, arg2); + } + + friend void* asio_handler_allocate(std::size_t size, + custom_alloc_handler * this_handler) + { + return this_handler->allocator_.allocate(size); + } + + friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/, + custom_alloc_handler * this_handler) + { + this_handler->allocator_.deallocate(pointer); + } + +private: + handler_allocator & allocator_; + Handler handler_; +}; + +// Helper function to wrap a handler object to add custom allocation. +template +inline custom_alloc_handler make_custom_alloc_handler( + handler_allocator & a, Handler h) +{ + return custom_alloc_handler(a, h); +} + + + + + + + +// Forward declaration of class endpoint so that it can be friended/referenced +// before being included. +template +class endpoint; + +typedef lib::function async_read_handler; + +typedef lib::function async_write_handler; + +typedef lib::function pre_init_handler; + +// handle_timer: dynamic parameters, multiple copies +// handle_proxy_write +// handle_proxy_read +// handle_async_write +// handle_pre_init + + +/// Asio transport errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// async_read_at_least call requested more bytes than buffer can store + invalid_num_bytes, + + /// there was an error in the underlying transport library + pass_through, + + /// The connection to the requested proxy server failed + proxy_failed, + + /// Invalid Proxy URI + proxy_invalid, + + /// Invalid host or service + invalid_host_service +}; + +/// Asio transport error category +class category : public lib::error_category { +public: + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.asio"; + } + + std::string message(int value) const { + switch(value) { + case error::general: + return "Generic asio transport policy error"; + case error::invalid_num_bytes: + return "async_read_at_least call requested more bytes than buffer can store"; + case error::pass_through: + return "Underlying Transport Error"; + case error::proxy_failed: + return "Proxy connection failed"; + case error::proxy_invalid: + return "Invalid proxy URI"; + case error::invalid_host_service: + return "Invalid host or service"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the asio transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Create an error code with the given value and the asio transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace asio +} // namespace transport +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ +#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/asio/connection.hpp b/subprojects/websocketpp/websocketpp/transport/asio/connection.hpp new file mode 100644 index 000000000..60f88a79f --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/asio/connection.hpp @@ -0,0 +1,1197 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { + +typedef lib::function tcp_init_handler; + +/// Asio based connection transport component +/** + * transport::asio::connection implements a connection transport component using + * Asio that works with the transport::asio::endpoint endpoint transport + * component. + */ +template +class connection : public config::socket_type::socket_con_type { +public: + /// Type of this connection transport component + typedef connection type; + /// Type of a shared pointer to this connection transport component + typedef lib::shared_ptr ptr; + + /// Type of the socket connection component + typedef typename config::socket_type::socket_con_type socket_con_type; + /// Type of a shared pointer to the socket connection component + typedef typename socket_con_type::ptr socket_con_ptr; + /// Type of this transport's access logging policy + typedef typename config::alog_type alog_type; + /// Type of this transport's error logging policy + typedef typename config::elog_type elog_type; + + typedef typename config::request_type request_type; + typedef typename request_type::ptr request_ptr; + typedef typename config::response_type response_type; + typedef typename response_type::ptr response_ptr; + + /// Type of a pointer to the Asio io_service being used + typedef lib::asio::io_service * io_service_ptr; + /// Type of a pointer to the Asio io_service::strand being used + typedef lib::shared_ptr strand_ptr; + /// Type of a pointer to the Asio timer class + typedef lib::shared_ptr timer_ptr; + + // connection is friends with its associated endpoint to allow the endpoint + // to call private/protected utility methods that we don't want to expose + // to the public api. + friend class endpoint; + + // generate and manage our own io_service + explicit connection(bool is_server, const lib::shared_ptr & alog, const lib::shared_ptr & elog) + : m_is_server(is_server) + , m_alog(alog) + , m_elog(elog) + { + m_alog->write(log::alevel::devel,"asio con transport constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return lib::static_pointer_cast(socket_con_type::get_shared()); + } + + bool is_secure() const { + return socket_con_type::is_secure(); + } + + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * This transport policy doesn't use the uri except to forward it to the + * socket layer. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr u) { + socket_con_type::set_uri(u); + } + + /// Sets the tcp pre init handler + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_pre_init_handler(tcp_init_handler h) { + m_tcp_pre_init_handler = h; + } + + /// Sets the tcp pre init handler (deprecated) + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @deprecated Use set_tcp_pre_init_handler instead + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_init_handler(tcp_init_handler h) { + set_tcp_pre_init_handler(h); + } + + /// Sets the tcp post init handler + /** + * The tcp post init handler is called after the tcp connection has been + * established and all additional wrappers (proxy connects, TLS handshakes, + * etc have been performed. This is fired before any bytes are read or any + * WebSocket specific handshake logic has been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp post init. + */ + void set_tcp_post_init_handler(tcp_init_handler h) { + m_tcp_post_init_handler = h; + } + + /// Set the proxy to connect through (exception free) + /** + * The URI passed should be a complete URI including scheme. For example: + * http://proxy.example.com:8080/ + * + * The proxy must be set up as an explicit (CONNECT) proxy allowed to + * connect to the port you specify. Traffic to the proxy is not encrypted. + * + * @param uri The full URI of the proxy to connect to. + * + * @param ec A status value + */ + void set_proxy(std::string const & uri, lib::error_code & ec) { + // TODO: return errors for illegal URIs here? + // TODO: should https urls be illegal for the moment? + m_proxy = uri; + m_proxy_data = lib::make_shared(); + ec = lib::error_code(); + } + + /// Set the proxy to connect through (exception) + void set_proxy(std::string const & uri) { + lib::error_code ec; + set_proxy(uri,ec); + if (ec) { throw exception(ec); } + } + + /// Set the basic auth credentials to use (exception free) + /** + * The URI passed should be a complete URI including scheme. For example: + * http://proxy.example.com:8080/ + * + * The proxy must be set up as an explicit proxy + * + * @param username The username to send + * + * @param password The password to send + * + * @param ec A status value + */ + void set_proxy_basic_auth(std::string const & username, std::string const & + password, lib::error_code & ec) + { + if (!m_proxy_data) { + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + // TODO: username can't contain ':' + std::string val = "Basic "+base64_encode(username + ":" + password); + m_proxy_data->req.replace_header("Proxy-Authorization",val); + ec = lib::error_code(); + } + + /// Set the basic auth credentials to use (exception) + void set_proxy_basic_auth(std::string const & username, std::string const & + password) + { + lib::error_code ec; + set_proxy_basic_auth(username,password,ec); + if (ec) { throw exception(ec); } + } + + /// Set the proxy timeout duration (exception free) + /** + * Duration is in milliseconds. Default value is based on the transport + * config + * + * @param duration The number of milliseconds to wait before aborting the + * proxy connection. + * + * @param ec A status value + */ + void set_proxy_timeout(long duration, lib::error_code & ec) { + if (!m_proxy_data) { + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_proxy_data->timeout_proxy = duration; + ec = lib::error_code(); + } + + /// Set the proxy timeout duration (exception) + void set_proxy_timeout(long duration) { + lib::error_code ec; + set_proxy_timeout(duration,ec); + if (ec) { throw exception(ec); } + } + + std::string const & get_proxy() const { + return m_proxy; + } + + /// Get the remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". To indicate + * this. + * + * TODO: allow user settable remote endpoint addresses if this seems useful + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint() const { + lib::error_code ec; + + std::string ret = socket_con_type::get_remote_endpoint(ec); + + if (ec) { + m_elog->write(log::elevel::info,ret); + return "Unknown"; + } else { + return ret; + } + } + + /// Get the connection handle + connection_hdl get_handle() const { + return m_connection_hdl; + } + + /// Call back a function after a period of time. + /** + * Sets a timer that calls back a function after the specified period of + * milliseconds. Returns a handle that can be used to cancel the timer. + * A cancelled timer will return the error code error::operation_aborted + * A timer that expired will return no error. + * + * @param duration Length of time to wait in milliseconds + * + * @param callback The function to call back when the timer has expired + * + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler callback) { + timer_ptr new_timer = lib::make_shared( + lib::ref(*m_io_service), + lib::asio::milliseconds(duration) + ); + + if (config::enable_multithreading) { + new_timer->async_wait(m_strand->wrap(lib::bind( + &type::handle_timer, get_shared(), + new_timer, + callback, + lib::placeholders::_1 + ))); + } else { + new_timer->async_wait(lib::bind( + &type::handle_timer, get_shared(), + new_timer, + callback, + lib::placeholders::_1 + )); + } + + return new_timer; + } + + /// Timer callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * TODO: candidate for protected status + * + * @param post_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec The status code + */ + void handle_timer(timer_ptr, timer_handler callback, + lib::asio::error_code const & ec) + { + if (ec) { + if (ec == lib::asio::error::operation_aborted) { + callback(make_error_code(transport::error::operation_aborted)); + } else { + log_err(log::elevel::info,"asio handle_timer",ec); + callback(make_error_code(error::pass_through)); + } + } else { + callback(lib::error_code()); + } + } + + /// Get a pointer to this connection's strand + strand_ptr get_strand() { + return m_strand; + } + + /// Get the internal transport error code for a closed/failed connection + /** + * Retrieves a machine readable detailed error code indicating the reason + * that the connection was closed or failed. Valid only after the close or + * fail handler is called. + * + * Primarily used if you are using mismatched asio / system_error + * implementations such as `boost::asio` with `std::system_error`. In these + * cases the transport error type is different than the library error type + * and some WebSocket++ functions that return transport errors via the + * library error code type will be coerced into a catch all `pass_through` + * or `tls_error` error. This method will return the original machine + * readable transport error in the native type. + * + * @since 0.7.0 + * + * @return Error code indicating the reason the connection was closed or + * failed + */ + lib::asio::error_code get_transport_ec() const { + return m_tec; + } + + /// Initialize transport for reading + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service + * + * The transport initialization sequence consists of the following steps: + * - Pre-init: the underlying socket is initialized to the point where + * bytes may be written. No bytes are actually written in this stage + * - Proxy negotiation: if a proxy is set, a request is made to it to start + * a tunnel to the final destination. This stage ends when the proxy is + * ready to forward the + * next byte to the remote endpoint. + * - Post-init: Perform any i/o with the remote endpoint, such as setting up + * tunnels for encryption. This stage ends when the connection is ready to + * read or write the WebSocket handshakes. At this point the original + * callback function is called. + */ +protected: + void init(init_handler callback) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"asio connection init"); + } + + // TODO: pre-init timeout. Right now no implemented socket policies + // actually have an asyncronous pre-init + + socket_con_type::pre_init( + lib::bind( + &type::handle_pre_init, + get_shared(), + callback, + lib::placeholders::_1 + ) + ); + } + + /// initialize the proxy buffers and http parsers + /** + * + * @param authority The address of the server we want the proxy to tunnel to + * in the format of a URI authority (host:port) + * + * @return Status code indicating what errors occurred, if any + */ + lib::error_code proxy_init(std::string const & authority) { + if (!m_proxy_data) { + return websocketpp::error::make_error_code( + websocketpp::error::invalid_state); + } + m_proxy_data->req.set_version("HTTP/1.1"); + m_proxy_data->req.set_method("CONNECT"); + + m_proxy_data->req.set_uri(authority); + m_proxy_data->req.replace_header("Host",authority); + + return lib::error_code(); + } + + /// Finish constructing the transport + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service. + * + * @param io_service A pointer to the io_service to register with this + * connection + * + * @return Status code for the success or failure of the initialization + */ + lib::error_code init_asio (io_service_ptr io_service) { + m_io_service = io_service; + + if (config::enable_multithreading) { + m_strand = lib::make_shared( + lib::ref(*io_service)); + } + + lib::error_code ec = socket_con_type::init_asio(io_service, m_strand, + m_is_server); + + return ec; + } + + void handle_pre_init(init_handler callback, lib::error_code const & ec) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"asio connection handle pre_init"); + } + + if (m_tcp_pre_init_handler) { + m_tcp_pre_init_handler(m_connection_hdl); + } + + if (ec) { + callback(ec); + } + + // If we have a proxy set issue a proxy connect, otherwise skip to + // post_init + if (!m_proxy.empty()) { + proxy_write(callback); + } else { + post_init(callback); + } + } + + void post_init(init_handler callback) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"asio connection post_init"); + } + + timer_ptr post_timer; + + if (config::timeout_socket_post_init > 0) { + post_timer = set_timer( + config::timeout_socket_post_init, + lib::bind( + &type::handle_post_init_timeout, + get_shared(), + post_timer, + callback, + lib::placeholders::_1 + ) + ); + } + + socket_con_type::post_init( + lib::bind( + &type::handle_post_init, + get_shared(), + post_timer, + callback, + lib::placeholders::_1 + ) + ); + } + + /// Post init timeout callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param post_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec The status code + */ + void handle_post_init_timeout(timer_ptr, init_handler callback, + lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio post init timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_post_init_timeout",ec); + ret_ec = ec; + } else { + if (socket_con_type::get_ec()) { + ret_ec = socket_con_type::get_ec(); + } else { + ret_ec = make_error_code(transport::error::timeout); + } + } + + m_alog->write(log::alevel::devel, "Asio transport post-init timed out"); + cancel_socket_checked(); + callback(ret_ec); + } + + /// Post init timeout callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param post_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec The status code + */ + void handle_post_init(timer_ptr post_timer, init_handler callback, + lib::error_code const & ec) + { + if (ec == transport::error::operation_aborted || + (post_timer && lib::asio::is_neg(post_timer->expires_from_now()))) + { + m_alog->write(log::alevel::devel,"post_init cancelled"); + return; + } + + if (post_timer) { + post_timer->cancel(); + } + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"asio connection handle_post_init"); + } + + if (m_tcp_post_init_handler) { + m_tcp_post_init_handler(m_connection_hdl); + } + + callback(ec); + } + + void proxy_write(init_handler callback) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"asio connection proxy_write"); + } + + if (!m_proxy_data) { + m_elog->write(log::elevel::library, + "assertion failed: !m_proxy_data in asio::connection::proxy_write"); + callback(make_error_code(error::general)); + return; + } + + m_proxy_data->write_buf = m_proxy_data->req.raw(); + + m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(), + m_proxy_data->write_buf.size())); + + m_alog->write(log::alevel::devel,m_proxy_data->write_buf); + + // Set a timer so we don't wait forever for the proxy to respond + m_proxy_data->timer = this->set_timer( + m_proxy_data->timeout_proxy, + lib::bind( + &type::handle_proxy_timeout, + get_shared(), + callback, + lib::placeholders::_1 + ) + ); + + // Send proxy request + if (config::enable_multithreading) { + lib::asio::async_write( + socket_con_type::get_next_layer(), + m_bufs, + m_strand->wrap(lib::bind( + &type::handle_proxy_write, get_shared(), + callback, + lib::placeholders::_1 + )) + ); + } else { + lib::asio::async_write( + socket_con_type::get_next_layer(), + m_bufs, + lib::bind( + &type::handle_proxy_write, get_shared(), + callback, + lib::placeholders::_1 + ) + ); + } + } + + void handle_proxy_timeout(init_handler callback, lib::error_code const & ec) + { + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio handle_proxy_write timer cancelled"); + return; + } else if (ec) { + log_err(log::elevel::devel,"asio handle_proxy_write",ec); + callback(ec); + } else { + m_alog->write(log::alevel::devel, + "asio handle_proxy_write timer expired"); + cancel_socket_checked(); + callback(make_error_code(transport::error::timeout)); + } + } + + void handle_proxy_write(init_handler callback, + lib::asio::error_code const & ec) + { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel, + "asio connection handle_proxy_write"); + } + + m_bufs.clear(); + + // Timer expired or the operation was aborted for some reason. + // Whatever aborted it will be issuing the callback so we are safe to + // return + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) + { + m_elog->write(log::elevel::devel,"write operation aborted"); + return; + } + + if (ec) { + log_err(log::elevel::info,"asio handle_proxy_write",ec); + m_proxy_data->timer->cancel(); + callback(make_error_code(error::pass_through)); + return; + } + + proxy_read(callback); + } + + void proxy_read(init_handler callback) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"asio connection proxy_read"); + } + + if (!m_proxy_data) { + m_elog->write(log::elevel::library, + "assertion failed: !m_proxy_data in asio::connection::proxy_read"); + m_proxy_data->timer->cancel(); + callback(make_error_code(error::general)); + return; + } + + if (config::enable_multithreading) { + lib::asio::async_read_until( + socket_con_type::get_next_layer(), + m_proxy_data->read_buf, + "\r\n\r\n", + m_strand->wrap(lib::bind( + &type::handle_proxy_read, get_shared(), + callback, + lib::placeholders::_1, lib::placeholders::_2 + )) + ); + } else { + lib::asio::async_read_until( + socket_con_type::get_next_layer(), + m_proxy_data->read_buf, + "\r\n\r\n", + lib::bind( + &type::handle_proxy_read, get_shared(), + callback, + lib::placeholders::_1, lib::placeholders::_2 + ) + ); + } + } + + /// Proxy read callback + /** + * @param init_handler The function to call back + * @param ec The status code + * @param bytes_transferred The number of bytes read + */ + void handle_proxy_read(init_handler callback, + lib::asio::error_code const & ec, size_t) + { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel, + "asio connection handle_proxy_read"); + } + + // Timer expired or the operation was aborted for some reason. + // Whatever aborted it will be issuing the callback so we are safe to + // return + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) + { + m_elog->write(log::elevel::devel,"read operation aborted"); + return; + } + + // At this point there is no need to wait for the timer anymore + m_proxy_data->timer->cancel(); + + if (ec) { + m_elog->write(log::elevel::info, + "asio handle_proxy_read error: "+ec.message()); + callback(make_error_code(error::pass_through)); + } else { + if (!m_proxy_data) { + m_elog->write(log::elevel::library, + "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read"); + callback(make_error_code(error::general)); + return; + } + + std::istream input(&m_proxy_data->read_buf); + + m_proxy_data->res.consume(input); + + if (!m_proxy_data->res.headers_ready()) { + // we read until the headers were done in theory but apparently + // they aren't. Internal endpoint error. + callback(make_error_code(error::general)); + return; + } + + m_alog->write(log::alevel::devel,m_proxy_data->res.raw()); + + if (m_proxy_data->res.get_status_code() != http::status_code::ok) { + // got an error response back + // TODO: expose this error in a programmatically accessible way? + // if so, see below for an option on how to do this. + std::stringstream s; + s << "Proxy connection error: " + << m_proxy_data->res.get_status_code() + << " (" + << m_proxy_data->res.get_status_msg() + << ")"; + m_elog->write(log::elevel::info,s.str()); + callback(make_error_code(error::proxy_failed)); + return; + } + + // we have successfully established a connection to the proxy, now + // we can continue and the proxy will transparently forward the + // WebSocket connection. + + // TODO: decide if we want an on_proxy callback that would allow + // access to the proxy response. + + // free the proxy buffers and req/res objects as they aren't needed + // anymore + m_proxy_data.reset(); + + // Continue with post proxy initialization + post_init(callback); + } + } + + /// read at least num_bytes bytes into buf and then call handler. + void async_read_at_least(size_t num_bytes, char *buf, size_t len, + read_handler handler) + { + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "asio async_read_at_least: " << num_bytes; + m_alog->write(log::alevel::devel,s.str()); + } + + // TODO: safety vs speed ? + // maybe move into an if devel block + /*if (num_bytes > len) { + m_elog->write(log::elevel::devel, + "asio async_read_at_least error::invalid_num_bytes"); + handler(make_error_code(transport::error::invalid_num_bytes), + size_t(0)); + return; + }*/ + + if (config::enable_multithreading) { + lib::asio::async_read( + socket_con_type::get_socket(), + lib::asio::buffer(buf,len), + lib::asio::transfer_at_least(num_bytes), + m_strand->wrap(make_custom_alloc_handler( + m_read_handler_allocator, + lib::bind( + &type::handle_async_read, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + )) + ); + } else { + lib::asio::async_read( + socket_con_type::get_socket(), + lib::asio::buffer(buf,len), + lib::asio::transfer_at_least(num_bytes), + make_custom_alloc_handler( + m_read_handler_allocator, + lib::bind( + &type::handle_async_read, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + ) + ); + } + + } + + void handle_async_read(read_handler handler, lib::asio::error_code const & ec, + size_t bytes_transferred) + { + m_alog->write(log::alevel::devel, "asio con handle_async_read"); + + // translate asio error codes into more lib::error_codes + lib::error_code tec; + if (ec == lib::asio::error::eof) { + tec = make_error_code(transport::error::eof); + } else if (ec) { + // We don't know much more about the error at this point. As our + // socket/security policy if it knows more: + tec = socket_con_type::translate_ec(ec); + m_tec = ec; + + if (tec == transport::error::tls_error || + tec == transport::error::pass_through) + { + // These are aggregate/catch all errors. Log some human readable + // information to the info channel to give library users some + // more details about why the upstream method may have failed. + log_err(log::elevel::info,"asio async_read_at_least",ec); + } + } + if (handler) { + handler(tec,bytes_transferred); + } else { + // This can happen in cases where the connection is terminated while + // the transport is waiting on a read. + m_alog->write(log::alevel::devel, + "handle_async_read called with null read handler"); + } + } + + /// Initiate a potentially asyncronous write of the given buffer + void async_write(const char* buf, size_t len, write_handler handler) { + m_bufs.push_back(lib::asio::buffer(buf,len)); + + if (config::enable_multithreading) { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + m_strand->wrap(make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + )) + ); + } else { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + ) + ); + } + } + + /// Initiate a potentially asyncronous write of the given buffers + void async_write(std::vector const & bufs, write_handler handler) { + std::vector::const_iterator it; + + for (it = bufs.begin(); it != bufs.end(); ++it) { + m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len)); + } + + if (config::enable_multithreading) { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + m_strand->wrap(make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + )) + ); + } else { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + ) + ); + } + } + + /// Async write callback + /** + * @param ec The status code + * @param bytes_transferred The number of bytes read + */ + void handle_async_write(write_handler handler, lib::asio::error_code const & ec, size_t) { + m_bufs.clear(); + lib::error_code tec; + if (ec) { + log_err(log::elevel::info,"asio async_write",ec); + tec = make_error_code(transport::error::pass_through); + } + if (handler) { + handler(tec); + } else { + // This can happen in cases where the connection is terminated while + // the transport is waiting on a read. + m_alog->write(log::alevel::devel, + "handle_async_write called with null write handler"); + } + } + + /// Set Connection Handle + /** + * See common/connection_hdl.hpp for information + * + * @param hdl A connection_hdl that the transport will use to refer + * to itself + */ + void set_handle(connection_hdl hdl) { + m_connection_hdl = hdl; + socket_con_type::set_handle(hdl); + } + + /// Trigger the on_interrupt handler + /** + * This needs to be thread safe + */ + lib::error_code interrupt(interrupt_handler handler) { + if (config::enable_multithreading) { + m_io_service->post(m_strand->wrap(handler)); + } else { + m_io_service->post(handler); + } + return lib::error_code(); + } + + lib::error_code dispatch(dispatch_handler handler) { + if (config::enable_multithreading) { + m_io_service->post(m_strand->wrap(handler)); + } else { + m_io_service->post(handler); + } + return lib::error_code(); + } + + /*void handle_interrupt(interrupt_handler handler) { + handler(); + }*/ + + /// close and clean up the underlying socket + void async_shutdown(shutdown_handler callback) { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel,"asio connection async_shutdown"); + } + + timer_ptr shutdown_timer; + shutdown_timer = set_timer( + config::timeout_socket_shutdown, + lib::bind( + &type::handle_async_shutdown_timeout, + get_shared(), + shutdown_timer, + callback, + lib::placeholders::_1 + ) + ); + + socket_con_type::async_shutdown( + lib::bind( + &type::handle_async_shutdown, + get_shared(), + shutdown_timer, + callback, + lib::placeholders::_1 + ) + ); + } + + /// Async shutdown timeout handler + /** + * @param shutdown_timer A pointer to the timer to keep it in scope + * @param callback The function to call back + * @param ec The status code + */ + void handle_async_shutdown_timeout(timer_ptr, init_handler callback, + lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio socket shutdown timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec); + ret_ec = ec; + } else { + ret_ec = make_error_code(transport::error::timeout); + } + + m_alog->write(log::alevel::devel, + "Asio transport socket shutdown timed out"); + cancel_socket_checked(); + callback(ret_ec); + } + + void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler + callback, lib::asio::error_code const & ec) + { + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(shutdown_timer->expires_from_now())) + { + m_alog->write(log::alevel::devel,"async_shutdown cancelled"); + return; + } + + shutdown_timer->cancel(); + + lib::error_code tec; + if (ec) { + if (ec == lib::asio::error::not_connected) { + // The socket was already closed when we tried to close it. This + // happens periodically (usually if a read or write fails + // earlier and if it is a real error will be caught at another + // level of the stack. + } else { + // We don't know anything more about this error, give our + // socket/security policy a crack at it. + tec = socket_con_type::translate_ec(ec); + m_tec = ec; + + // all other errors are effectively pass through errors of + // some sort so print some detail on the info channel for + // library users to look up if needed. + log_err(log::elevel::info,"asio async_shutdown",ec); + } + } else { + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel, + "asio con handle_async_shutdown"); + } + } + callback(tec); + } + + /// Cancel the underlying socket and log any errors + void cancel_socket_checked() { + lib::asio::error_code cec = socket_con_type::cancel_socket(); + if (cec) { + if (cec == lib::asio::error::operation_not_supported) { + // cancel not supported on this OS, ignore and log at dev level + m_alog->write(log::alevel::devel, "socket cancel not supported"); + } else { + log_err(log::elevel::warn, "socket cancel failed", cec); + } + } + } + +private: + /// Convenience method for logging the code and message for an error_code + template + void log_err(log::level l, const char * msg, const error_type & ec) { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog->write(l,s.str()); + } + + // static settings + const bool m_is_server; + lib::shared_ptr m_alog; + lib::shared_ptr m_elog; + + struct proxy_data { + proxy_data() : timeout_proxy(config::timeout_proxy) {} + + request_type req; + response_type res; + std::string write_buf; + lib::asio::streambuf read_buf; + long timeout_proxy; + timer_ptr timer; + }; + + std::string m_proxy; + lib::shared_ptr m_proxy_data; + + // transport resources + io_service_ptr m_io_service; + strand_ptr m_strand; + connection_hdl m_connection_hdl; + + std::vector m_bufs; + + /// Detailed internal error code + lib::asio::error_code m_tec; + + // Handlers + tcp_init_handler m_tcp_pre_init_handler; + tcp_init_handler m_tcp_post_init_handler; + + handler_allocator m_read_handler_allocator; + handler_allocator m_write_handler_allocator; +}; + + +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/asio/endpoint.hpp b/subprojects/websocketpp/websocketpp/transport/asio/endpoint.hpp new file mode 100644 index 000000000..ddab2c742 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/asio/endpoint.hpp @@ -0,0 +1,1186 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_HPP + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { + +/// Asio based endpoint transport component +/** + * transport::asio::endpoint implements an endpoint transport component using + * Asio. + */ +template +class endpoint : public config::socket_type { +public: + /// Type of this endpoint transport component + typedef endpoint type; + + /// Type of the concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of the socket policy + typedef typename config::socket_type socket_type; + /// Type of the error logging policy + typedef typename config::elog_type elog_type; + /// Type of the access logging policy + typedef typename config::alog_type alog_type; + + /// Type of the socket connection component + typedef typename socket_type::socket_con_type socket_con_type; + /// Type of a shared pointer to the socket connection component + typedef typename socket_con_type::ptr socket_con_ptr; + + /// Type of the connection transport component associated with this + /// endpoint transport component + typedef asio::connection transport_con_type; + /// Type of a shared pointer to the connection transport component + /// associated with this endpoint transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of a pointer to the ASIO io_service being used + typedef lib::asio::io_service * io_service_ptr; + /// Type of a shared pointer to the acceptor being used + typedef lib::shared_ptr acceptor_ptr; + /// Type of a shared pointer to the resolver being used + typedef lib::shared_ptr resolver_ptr; + /// Type of timer handle + typedef lib::shared_ptr timer_ptr; + /// Type of a shared pointer to an io_service work object + typedef lib::shared_ptr work_ptr; + + /// Type of socket pre-bind handler + typedef lib::function tcp_pre_bind_handler; + + // generate and manage our own io_service + explicit endpoint() + : m_io_service(NULL) + , m_external_io_service(false) + , m_listen_backlog(lib::asio::socket_base::max_connections) + , m_reuse_addr(false) + , m_state(UNINITIALIZED) + { + //std::cout << "transport::asio::endpoint constructor" << std::endl; + } + + ~endpoint() { + // clean up our io_service if we were initialized with an internal one. + + // Explicitly destroy local objects + m_acceptor.reset(); + m_resolver.reset(); + m_work.reset(); + if (m_state != UNINITIALIZED && !m_external_io_service) { + delete m_io_service; + } + } + + /// transport::asio objects are moveable but not copyable or assignable. + /// The following code sets this situation up based on whether or not we + /// have C++11 support or not +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + endpoint(const endpoint & src) = delete; + endpoint& operator= (const endpoint & rhs) = delete; +#else +private: + endpoint(const endpoint & src); + endpoint & operator= (const endpoint & rhs); +public: +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + endpoint (endpoint && src) + : config::socket_type(std::move(src)) + , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler) + , m_tcp_post_init_handler(src.m_tcp_post_init_handler) + , m_io_service(src.m_io_service) + , m_external_io_service(src.m_external_io_service) + , m_acceptor(src.m_acceptor) + , m_listen_backlog(lib::asio::socket_base::max_connections) + , m_reuse_addr(src.m_reuse_addr) + , m_elog(src.m_elog) + , m_alog(src.m_alog) + , m_state(src.m_state) + { + src.m_io_service = NULL; + src.m_external_io_service = false; + src.m_acceptor = NULL; + src.m_state = UNINITIALIZED; + } + + /*endpoint & operator= (const endpoint && rhs) { + if (this != &rhs) { + m_io_service = rhs.m_io_service; + m_external_io_service = rhs.m_external_io_service; + m_acceptor = rhs.m_acceptor; + m_listen_backlog = rhs.m_listen_backlog; + m_reuse_addr = rhs.m_reuse_addr; + m_state = rhs.m_state; + + rhs.m_io_service = NULL; + rhs.m_external_io_service = false; + rhs.m_acceptor = NULL; + rhs.m_listen_backlog = lib::asio::socket_base::max_connections; + rhs.m_state = UNINITIALIZED; + + // TODO: this needs to be updated + } + return *this; + }*/ +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + /// Return whether or not the endpoint produces secure connections. + bool is_secure() const { + return socket_type::is_secure(); + } + + /// initialize asio transport with external io_service (exception free) + /** + * Initialize the ASIO transport policy for this endpoint using the provided + * io_service object. asio_init must be called exactly once on any endpoint + * that uses transport::asio before it can be used. + * + * @param ptr A pointer to the io_service to use for asio events + * @param ec Set to indicate what error occurred, if any. + */ + void init_asio(io_service_ptr ptr, lib::error_code & ec) { + if (m_state != UNINITIALIZED) { + m_elog->write(log::elevel::library, + "asio::init_asio called from the wrong state"); + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_alog->write(log::alevel::devel,"asio::init_asio"); + + m_io_service = ptr; + m_external_io_service = true; + m_acceptor = lib::make_shared( + lib::ref(*m_io_service)); + + m_state = READY; + ec = lib::error_code(); + } + + /// initialize asio transport with external io_service + /** + * Initialize the ASIO transport policy for this endpoint using the provided + * io_service object. asio_init must be called exactly once on any endpoint + * that uses transport::asio before it can be used. + * + * @param ptr A pointer to the io_service to use for asio events + */ + void init_asio(io_service_ptr ptr) { + lib::error_code ec; + init_asio(ptr,ec); + if (ec) { throw exception(ec); } + } + + /// Initialize asio transport with internal io_service (exception free) + /** + * This method of initialization will allocate and use an internally managed + * io_service. + * + * @see init_asio(io_service_ptr ptr) + * + * @param ec Set to indicate what error occurred, if any. + */ + void init_asio(lib::error_code & ec) { + // Use a smart pointer until the call is successful and ownership has + // successfully been taken. Use unique_ptr when available. + // TODO: remove the use of auto_ptr when C++98/03 support is no longer + // necessary. +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + lib::unique_ptr service(new lib::asio::io_service()); +#else + lib::auto_ptr service(new lib::asio::io_service()); +#endif + init_asio(service.get(), ec); + if( !ec ) service.release(); // Call was successful, transfer ownership + m_external_io_service = false; + } + + /// Initialize asio transport with internal io_service + /** + * This method of initialization will allocate and use an internally managed + * io_service. + * + * @see init_asio(io_service_ptr ptr) + */ + void init_asio() { + // Use a smart pointer until the call is successful and ownership has + // successfully been taken. Use unique_ptr when available. + // TODO: remove the use of auto_ptr when C++98/03 support is no longer + // necessary. +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + lib::unique_ptr service(new lib::asio::io_service()); +#else + lib::auto_ptr service(new lib::asio::io_service()); +#endif + init_asio( service.get() ); + // If control got this far without an exception, then ownership has successfully been taken + service.release(); + m_external_io_service = false; + } + + /// Sets the tcp pre bind handler + /** + * The tcp pre bind handler is called after the listen acceptor has + * been created but before the socket bind is performed. + * + * @since 0.8.0 + * + * @param h The handler to call on tcp pre bind init. + */ + void set_tcp_pre_bind_handler(tcp_pre_bind_handler h) { + m_tcp_pre_bind_handler = h; + } + + /// Sets the tcp pre init handler + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_pre_init_handler(tcp_init_handler h) { + m_tcp_pre_init_handler = h; + } + + /// Sets the tcp pre init handler (deprecated) + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @deprecated Use set_tcp_pre_init_handler instead + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_init_handler(tcp_init_handler h) { + set_tcp_pre_init_handler(h); + } + + /// Sets the tcp post init handler + /** + * The tcp post init handler is called after the tcp connection has been + * established and all additional wrappers (proxy connects, TLS handshakes, + * etc have been performed. This is fired before any bytes are read or any + * WebSocket specific handshake logic has been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp post init. + */ + void set_tcp_post_init_handler(tcp_init_handler h) { + m_tcp_post_init_handler = h; + } + + /// Sets the maximum length of the queue of pending connections. + /** + * Sets the maximum length of the queue of pending connections. Increasing + * this will allow WebSocket++ to queue additional incoming connections. + * Setting it higher may prevent failed connections at high connection rates + * but may cause additional latency. + * + * For this value to take effect you may need to adjust operating system + * settings. + * + * New values affect future calls to listen only. + * + * The default value is specified as *::asio::socket_base::max_connections + * which uses the operating system defined maximum queue length. Your OS + * may restrict or silently lower this value. A value of zero may cause + * all connections to be rejected. + * + * @since 0.3.0 + * + * @param backlog The maximum length of the queue of pending connections + */ + void set_listen_backlog(int backlog) { + m_listen_backlog = backlog; + } + + /// Sets whether to use the SO_REUSEADDR flag when opening listening sockets + /** + * Specifies whether or not to use the SO_REUSEADDR TCP socket option. What + * this flag does depends on your operating system. + * + * Please consult operating system documentation for more details. There + * may be security consequences to enabling this option. + * + * New values affect future calls to listen only so set this value prior to + * calling listen. + * + * The default is false. + * + * @since 0.3.0 + * + * @param value Whether or not to use the SO_REUSEADDR option + */ + void set_reuse_addr(bool value) { + m_reuse_addr = value; + } + + /// Retrieve a reference to the endpoint's io_service + /** + * The io_service may be an internal or external one. This may be used to + * call methods of the io_service that are not explicitly wrapped by the + * endpoint. + * + * This method is only valid after the endpoint has been initialized with + * `init_asio`. No error will be returned if it isn't. + * + * @return A reference to the endpoint's io_service + */ + lib::asio::io_service & get_io_service() { + return *m_io_service; + } + + /// Get local TCP endpoint + /** + * Extracts the local endpoint from the acceptor. This represents the + * address that WebSocket++ is listening on. + * + * Sets a bad_descriptor error if the acceptor is not currently listening + * or otherwise unavailable. + * + * @since 0.7.0 + * + * @param ec Set to indicate what error occurred, if any. + * @return The local endpoint + */ + lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) { + if (m_acceptor) { + return m_acceptor->local_endpoint(ec); + } else { + ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor); + return lib::asio::ip::tcp::endpoint(); + } + } + + /// Set up endpoint for listening manually (exception free) + /** + * Bind the internal acceptor using the specified settings. The endpoint + * must have been initialized by calling init_asio before listening. + * + * @param ep An endpoint to read settings from + * @param ec Set to indicate what error occurred, if any. + */ + void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec) + { + if (m_state != READY) { + m_elog->write(log::elevel::library, + "asio::listen called from the wrong state"); + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_alog->write(log::alevel::devel,"asio::listen"); + + lib::asio::error_code bec; + + m_acceptor->open(ep.protocol(),bec); + if (bec) {ec = clean_up_listen_after_error(bec);return;} + + m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec); + if (bec) {ec = clean_up_listen_after_error(bec);return;} + + // if a TCP pre-bind handler is present, run it + if (m_tcp_pre_bind_handler) { + ec = m_tcp_pre_bind_handler(m_acceptor); + if (ec) { + ec = clean_up_listen_after_error(ec); + return; + } + } + + m_acceptor->bind(ep,bec); + if (bec) {ec = clean_up_listen_after_error(bec);return;} + + m_acceptor->listen(m_listen_backlog,bec); + if (bec) {ec = clean_up_listen_after_error(bec);return;} + + // Success + m_state = LISTENING; + ec = lib::error_code(); + } + + + + /// Set up endpoint for listening manually + /** + * Bind the internal acceptor using the settings specified by the endpoint e + * + * @param ep An endpoint to read settings from + */ + void listen(lib::asio::ip::tcp::endpoint const & ep) { + lib::error_code ec; + listen(ep,ec); + if (ec) { throw exception(ec); } + } + + /// Set up endpoint for listening with protocol and port (exception free) + /** + * Bind the internal acceptor using the given internet protocol and port. + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * Common options include: + * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() + * - IPv4 only: lib::asio::ip::tcp::v4() + * + * @param internet_protocol The internet protocol to use. + * @param port The port to listen on. + * @param ec Set to indicate what error occurred, if any. + */ + template + void listen(InternetProtocol const & internet_protocol, uint16_t port, + lib::error_code & ec) + { + lib::asio::ip::tcp::endpoint ep(internet_protocol, port); + listen(ep,ec); + } + + /// Set up endpoint for listening with protocol and port + /** + * Bind the internal acceptor using the given internet protocol and port. + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * Common options include: + * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() + * - IPv4 only: lib::asio::ip::tcp::v4() + * + * @param internet_protocol The internet protocol to use. + * @param port The port to listen on. + */ + template + void listen(InternetProtocol const & internet_protocol, uint16_t port) + { + lib::asio::ip::tcp::endpoint ep(internet_protocol, port); + listen(ep); + } + + /// Set up endpoint for listening on a port (exception free) + /** + * Bind the internal acceptor using the given port. The IPv6 protocol with + * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use + * the overload that allows specifying the protocol explicitly. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param port The port to listen on. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(uint16_t port, lib::error_code & ec) { + listen(lib::asio::ip::tcp::v6(), port, ec); + } + + /// Set up endpoint for listening on a port + /** + * Bind the internal acceptor using the given port. The IPv6 protocol with + * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use + * the overload that allows specifying the protocol explicitly. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param port The port to listen on. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(uint16_t port) { + listen(lib::asio::ip::tcp::v6(), port); + } + + /// Set up endpoint for listening on a host and service (exception free) + /** + * Bind the internal acceptor using the given host and service. More details + * about what host and service can be are available in the Asio + * documentation for ip::basic_resolver_query::basic_resolver_query's + * constructors. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(std::string const & host, std::string const & service, + lib::error_code & ec) + { + using lib::asio::ip::tcp; + tcp::resolver r(*m_io_service); + tcp::resolver::query query(host, service); + tcp::resolver::iterator endpoint_iterator = r.resolve(query); + tcp::resolver::iterator end; + if (endpoint_iterator == end) { + m_elog->write(log::elevel::library, + "asio::listen could not resolve the supplied host or service"); + ec = make_error_code(error::invalid_host_service); + return; + } + listen(*endpoint_iterator,ec); + } + + /// Set up endpoint for listening on a host and service + /** + * Bind the internal acceptor using the given host and service. More details + * about what host and service can be are available in the Asio + * documentation for ip::basic_resolver_query::basic_resolver_query's + * constructors. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(std::string const & host, std::string const & service) + { + lib::error_code ec; + listen(host,service,ec); + if (ec) { throw exception(ec); } + } + + /// Stop listening (exception free) + /** + * Stop listening and accepting new connections. This will not end any + * existing connections. + * + * @since 0.3.0-alpha4 + * @param ec A status code indicating an error, if any. + */ + void stop_listening(lib::error_code & ec) { + if (m_state != LISTENING) { + m_elog->write(log::elevel::library, + "asio::listen called from the wrong state"); + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_acceptor->close(); + m_state = READY; + ec = lib::error_code(); + } + + /// Stop listening + /** + * Stop listening and accepting new connections. This will not end any + * existing connections. + * + * @since 0.3.0-alpha4 + */ + void stop_listening() { + lib::error_code ec; + stop_listening(ec); + if (ec) { throw exception(ec); } + } + + /// Check if the endpoint is listening + /** + * @return Whether or not the endpoint is listening. + */ + bool is_listening() const { + return (m_state == LISTENING); + } + + /// wraps the run method of the internal io_service object + std::size_t run() { + return m_io_service->run(); + } + + /// wraps the run_one method of the internal io_service object + /** + * @since 0.3.0-alpha4 + */ + std::size_t run_one() { + return m_io_service->run_one(); + } + + /// wraps the stop method of the internal io_service object + void stop() { + m_io_service->stop(); + } + + /// wraps the poll method of the internal io_service object + std::size_t poll() { + return m_io_service->poll(); + } + + /// wraps the poll_one method of the internal io_service object + std::size_t poll_one() { + return m_io_service->poll_one(); + } + + /// wraps the reset method of the internal io_service object + void reset() { + m_io_service->reset(); + } + + /// wraps the stopped method of the internal io_service object + bool stopped() const { + return m_io_service->stopped(); + } + + /// Marks the endpoint as perpetual, stopping it from exiting when empty + /** + * Marks the endpoint as perpetual. Perpetual endpoints will not + * automatically exit when they run out of connections to process. To stop + * a perpetual endpoint call `end_perpetual`. + * + * An endpoint may be marked perpetual at any time by any thread. It must be + * called either before the endpoint has run out of work or before it was + * started + * + * @since 0.3.0 + */ + void start_perpetual() { + m_work = lib::make_shared( + lib::ref(*m_io_service) + ); + } + + /// Clears the endpoint's perpetual flag, allowing it to exit when empty + /** + * Clears the endpoint's perpetual flag. This will cause the endpoint's run + * method to exit normally when it runs out of connections. If there are + * currently active connections it will not end until they are complete. + * + * @since 0.3.0 + */ + void stop_perpetual() { + m_work.reset(); + } + + /// Call back a function after a period of time. + /** + * Sets a timer that calls back a function after the specified period of + * milliseconds. Returns a handle that can be used to cancel the timer. + * A cancelled timer will return the error code error::operation_aborted + * A timer that expired will return no error. + * + * @param duration Length of time to wait in milliseconds + * @param callback The function to call back when the timer has expired + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler callback) { + timer_ptr new_timer = lib::make_shared( + *m_io_service, + lib::asio::milliseconds(duration) + ); + + new_timer->async_wait( + lib::bind( + &type::handle_timer, + this, + new_timer, + callback, + lib::placeholders::_1 + ) + ); + + return new_timer; + } + + /// Timer handler + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param t Pointer to the timer in question + * @param callback The function to call back + * @param ec A status code indicating an error, if any. + */ + void handle_timer(timer_ptr, timer_handler callback, + lib::asio::error_code const & ec) + { + if (ec) { + if (ec == lib::asio::error::operation_aborted) { + callback(make_error_code(transport::error::operation_aborted)); + } else { + m_elog->write(log::elevel::info, + "asio handle_timer error: "+ec.message()); + log_err(log::elevel::info,"asio handle_timer",ec); + callback(socket_con_type::translate_ec(ec)); + } + } else { + callback(lib::error_code()); + } + } + + /// Accept the next connection attempt and assign it to con (exception free) + /** + * @param tcon The connection to accept into. + * @param callback The function to call when the operation is complete. + * @param ec A status code indicating an error, if any. + */ + void async_accept(transport_con_ptr tcon, accept_handler callback, + lib::error_code & ec) + { + if (m_state != LISTENING || !m_acceptor) { + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::async_accept_not_listening); + return; + } + + m_alog->write(log::alevel::devel, "asio::async_accept"); + + if (config::enable_multithreading) { + m_acceptor->async_accept( + tcon->get_raw_socket(), + tcon->get_strand()->wrap(lib::bind( + &type::handle_accept, + this, + callback, + lib::placeholders::_1 + )) + ); + } else { + m_acceptor->async_accept( + tcon->get_raw_socket(), + lib::bind( + &type::handle_accept, + this, + callback, + lib::placeholders::_1 + ) + ); + } + } + + /// Accept the next connection attempt and assign it to con. + /** + * @param tcon The connection to accept into. + * @param callback The function to call when the operation is complete. + */ + void async_accept(transport_con_ptr tcon, accept_handler callback) { + lib::error_code ec; + async_accept(tcon,callback,ec); + if (ec) { throw exception(ec); } + } +protected: + /// Initialize logging + /** + * The loggers are located in the main endpoint class. As such, the + * transport doesn't have direct access to them. This method is called + * by the endpoint constructor to allow shared logging from the transport + * component. These are raw pointers to member variables of the endpoint. + * In particular, they cannot be used in the transport constructor as they + * haven't been constructed yet, and cannot be used in the transport + * destructor as they will have been destroyed by then. + */ + void init_logging(const lib::shared_ptr& a, const lib::shared_ptr& e) { + m_alog = a; + m_elog = e; + } + + void handle_accept(accept_handler callback, lib::asio::error_code const & + asio_ec) + { + lib::error_code ret_ec; + + m_alog->write(log::alevel::devel, "asio::handle_accept"); + + if (asio_ec) { + if (asio_ec == lib::asio::errc::operation_canceled) { + ret_ec = make_error_code(websocketpp::error::operation_canceled); + } else { + log_err(log::elevel::info,"asio handle_accept",asio_ec); + ret_ec = socket_con_type::translate_ec(asio_ec); + } + } + + callback(ret_ec); + } + + /// Initiate a new connection + // TODO: there have to be some more failure conditions here + void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) { + using namespace lib::asio::ip; + + // Create a resolver + if (!m_resolver) { + m_resolver = lib::make_shared( + lib::ref(*m_io_service)); + } + + tcon->set_uri(u); + + std::string proxy = tcon->get_proxy(); + std::string host; + std::string port; + + if (proxy.empty()) { + host = u->get_host(); + port = u->get_port_str(); + } else { + lib::error_code ec; + + uri_ptr pu = lib::make_shared(proxy); + + if (!pu->get_valid()) { + cb(make_error_code(error::proxy_invalid)); + return; + } + + ec = tcon->proxy_init(u->get_authority()); + if (ec) { + cb(ec); + return; + } + + host = pu->get_host(); + port = pu->get_port_str(); + } + + tcp::resolver::query query(host,port); + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel, + "starting async DNS resolve for "+host+":"+port); + } + + timer_ptr dns_timer; + + dns_timer = tcon->set_timer( + config::timeout_dns_resolve, + lib::bind( + &type::handle_resolve_timeout, + this, + dns_timer, + cb, + lib::placeholders::_1 + ) + ); + + if (config::enable_multithreading) { + m_resolver->async_resolve( + query, + tcon->get_strand()->wrap(lib::bind( + &type::handle_resolve, + this, + tcon, + dns_timer, + cb, + lib::placeholders::_1, + lib::placeholders::_2 + )) + ); + } else { + m_resolver->async_resolve( + query, + lib::bind( + &type::handle_resolve, + this, + tcon, + dns_timer, + cb, + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } + } + + /// DNS resolution timeout handler + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param dns_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec A status code indicating an error, if any. + */ + void handle_resolve_timeout(timer_ptr, connect_handler callback, + lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio handle_resolve_timeout timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_resolve_timeout",ec); + ret_ec = ec; + } else { + ret_ec = make_error_code(transport::error::timeout); + } + + m_alog->write(log::alevel::devel,"DNS resolution timed out"); + m_resolver->cancel(); + callback(ret_ec); + } + + void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer, + connect_handler callback, lib::asio::error_code const & ec, + lib::asio::ip::tcp::resolver::iterator iterator) + { + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(dns_timer->expires_from_now())) + { + m_alog->write(log::alevel::devel,"async_resolve cancelled"); + return; + } + + dns_timer->cancel(); + + if (ec) { + log_err(log::elevel::info,"asio async_resolve",ec); + callback(socket_con_type::translate_ec(ec)); + return; + } + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "Async DNS resolve successful. Results: "; + + lib::asio::ip::tcp::resolver::iterator it, end; + for (it = iterator; it != end; ++it) { + s << (*it).endpoint() << " "; + } + + m_alog->write(log::alevel::devel,s.str()); + } + + m_alog->write(log::alevel::devel,"Starting async connect"); + + timer_ptr con_timer; + + con_timer = tcon->set_timer( + config::timeout_connect, + lib::bind( + &type::handle_connect_timeout, + this, + tcon, + con_timer, + callback, + lib::placeholders::_1 + ) + ); + + if (config::enable_multithreading) { + lib::asio::async_connect( + tcon->get_raw_socket(), + iterator, + tcon->get_strand()->wrap(lib::bind( + &type::handle_connect, + this, + tcon, + con_timer, + callback, + lib::placeholders::_1 + )) + ); + } else { + lib::asio::async_connect( + tcon->get_raw_socket(), + iterator, + lib::bind( + &type::handle_connect, + this, + tcon, + con_timer, + callback, + lib::placeholders::_1 + ) + ); + } + } + + /// Asio connect timeout handler + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param tcon Pointer to the transport connection that is being connected + * @param con_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec A status code indicating an error, if any. + */ + void handle_connect_timeout(transport_con_ptr tcon, timer_ptr, + connect_handler callback, lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio handle_connect_timeout timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_connect_timeout",ec); + ret_ec = ec; + } else { + ret_ec = make_error_code(transport::error::timeout); + } + + m_alog->write(log::alevel::devel,"TCP connect timed out"); + tcon->cancel_socket_checked(); + callback(ret_ec); + } + + void handle_connect(transport_con_ptr tcon, timer_ptr con_timer, + connect_handler callback, lib::asio::error_code const & ec) + { + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(con_timer->expires_from_now())) + { + m_alog->write(log::alevel::devel,"async_connect cancelled"); + return; + } + + con_timer->cancel(); + + if (ec) { + log_err(log::elevel::info,"asio async_connect",ec); + callback(socket_con_type::translate_ec(ec)); + return; + } + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel, + "Async connect to "+tcon->get_remote_endpoint()+" successful."); + } + + callback(lib::error_code()); + } + + /// Initialize a connection + /** + * init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * @param tcon A pointer to the transport portion of the connection. + * + * @return A status code indicating the success or failure of the operation + */ + lib::error_code init(transport_con_ptr tcon) { + m_alog->write(log::alevel::devel, "transport::asio::init"); + + // Initialize the connection socket component + socket_type::init(lib::static_pointer_cast(tcon)); + + lib::error_code ec; + + ec = tcon->init_asio(m_io_service); + if (ec) {return ec;} + + tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler); + tcon->set_tcp_post_init_handler(m_tcp_post_init_handler); + + return lib::error_code(); + } +private: + /// Convenience method for logging the code and message for an error_code + template + void log_err(log::level l, char const * msg, error_type const & ec) { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog->write(l,s.str()); + } + + /// Helper for cleaning up in the listen method after an error + template + lib::error_code clean_up_listen_after_error(error_type const & ec) { + if (m_acceptor->is_open()) { + m_acceptor->close(); + } + log_err(log::elevel::info,"asio listen",ec); + return socket_con_type::translate_ec(ec); + } + + enum state { + UNINITIALIZED = 0, + READY = 1, + LISTENING = 2 + }; + + // Handlers + tcp_pre_bind_handler m_tcp_pre_bind_handler; + tcp_init_handler m_tcp_pre_init_handler; + tcp_init_handler m_tcp_post_init_handler; + + // Network Resources + io_service_ptr m_io_service; + bool m_external_io_service; + acceptor_ptr m_acceptor; + resolver_ptr m_resolver; + work_ptr m_work; + + // Network constants + int m_listen_backlog; + bool m_reuse_addr; + + lib::shared_ptr m_elog; + lib::shared_ptr m_alog; + + // Transport state + state m_state; +}; + +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/asio/security/base.hpp b/subprojects/websocketpp/websocketpp/transport/asio/security/base.hpp new file mode 100644 index 000000000..0f08f4046 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/asio/security/base.hpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP + +#include +#include +#include +#include +#include +#include + +#include + +// Interface that sockets/security policies must implement + +/* + * Endpoint Interface + * + * bool is_secure() const; + * @return Whether or not the endpoint creates secure connections + * + * lib::error_code init(socket_con_ptr scon); + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. + * @param scon Pointer to the socket component of the connection + * @return Error code (empty on success) + */ + + +// Connection +// TODO +// set_hostname(std::string hostname) +// pre_init(init_handler); +// post_init(init_handler); + +namespace websocketpp { +namespace transport { +namespace asio { +namespace socket { + +typedef lib::function shutdown_handler; + +/** + * The transport::asio::socket::* classes are a set of security/socket related + * policies and support code for the ASIO transport types. + */ + +/// Errors related to asio transport sockets +namespace error { + enum value { + /// Catch-all error for security policy errors that don't fit in other + /// categories + security = 1, + + /// Catch-all error for socket component errors that don't fit in other + /// categories + socket, + + /// A function was called in a state that it was illegal to do so. + invalid_state, + + /// The application was prompted to provide a TLS context and it was + /// empty or otherwise invalid + invalid_tls_context, + + /// TLS Handshake Timeout + tls_handshake_timeout, + + /// pass_through from underlying library + pass_through, + + /// Required tls_init handler not present + missing_tls_init_handler, + + /// TLS Handshake Failed + tls_handshake_failed, + + /// Failed to set TLS SNI hostname + tls_failed_sni_hostname + }; +} // namespace error + +/// Error category related to asio transport socket policies +class socket_category : public lib::error_category { +public: + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.asio.socket"; + } + + std::string message(int value) const { + switch(value) { + case error::security: + return "Security policy error"; + case error::socket: + return "Socket component error"; + case error::invalid_state: + return "Invalid state"; + case error::invalid_tls_context: + return "Invalid or empty TLS context supplied"; + case error::tls_handshake_timeout: + return "TLS handshake timed out"; + case error::pass_through: + return "Pass through from socket policy"; + case error::missing_tls_init_handler: + return "Required tls_init handler not present."; + case error::tls_handshake_failed: + return "TLS handshake failed"; + case error::tls_failed_sni_hostname: + return "Failed to set TLS SNI hostname"; + default: + return "Unknown"; + } + } +}; + +inline lib::error_category const & get_socket_category() { + static socket_category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_socket_category()); +} + +/// Type of asio transport socket policy initialization handlers +typedef lib::function init_handler; + +} // namespace socket +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/asio/security/none.hpp b/subprojects/websocketpp/websocketpp/transport/asio/security/none.hpp new file mode 100644 index 000000000..5c8293dbe --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/asio/security/none.hpp @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP +#define WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { +/// A socket policy for the asio transport that implements a plain, unencrypted +/// socket +namespace basic_socket { + +/// The signature of the socket init handler for this socket policy +typedef lib::function + socket_init_handler; + +/// Basic Asio connection socket component +/** + * transport::asio::basic_socket::connection implements a connection socket + * component using Asio ip::tcp::socket. + */ +class connection : public lib::enable_shared_from_this { +public: + /// Type of this connection socket component + typedef connection type; + /// Type of a shared pointer to this connection socket component + typedef lib::shared_ptr ptr; + + /// Type of a pointer to the Asio io_service being used + typedef lib::asio::io_service* io_service_ptr; + /// Type of a pointer to the Asio io_service strand being used + typedef lib::shared_ptr strand_ptr; + /// Type of the ASIO socket being used + typedef lib::asio::ip::tcp::socket socket_type; + /// Type of a shared pointer to the socket being used. + typedef lib::shared_ptr socket_ptr; + + explicit connection() : m_state(UNINITIALIZED) { + //std::cout << "transport::asio::basic_socket::connection constructor" + // << std::endl; + } + + /// Get a shared pointer to this component + ptr get_shared() { + return shared_from_this(); + } + + /// Check whether or not this connection is secure + /** + * @return Whether or not this connection is secure + */ + bool is_secure() const { + return false; + } + + /// Set the socket initialization handler + /** + * The socket initialization handler is called after the socket object is + * created but before it is used. This gives the application a chance to + * set any Asio socket options it needs. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. It can also be used to set socket options, etc + */ + lib::asio::ip::tcp::socket & get_socket() { + return *m_socket; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. + */ + lib::asio::ip::tcp::socket & get_next_layer() { + return *m_socket; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. It can also be used to set socket options, etc + */ + lib::asio::ip::tcp::socket & get_raw_socket() { + return *m_socket; + } + + /// Get the remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". To indicate + * this. + * + * TODO: allow user settable remote endpoint addresses if this seems useful + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint(lib::error_code & ec) const { + std::stringstream s; + + lib::asio::error_code aec; + lib::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(aec); + + if (aec) { + ec = error::make_error_code(error::pass_through); + s << "Error getting remote endpoint: " << aec + << " (" << aec.message() << ")"; + return s.str(); + } else { + ec = lib::error_code(); + s << ep; + return s.str(); + } + } +protected: + /// Perform one time initializations + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service + * + * @param service A pointer to the endpoint's io_service + * @param strand A shared pointer to the connection's asio strand + * @param is_server Whether or not the endpoint is a server or not. + */ + lib::error_code init_asio (io_service_ptr service, strand_ptr, bool) + { + if (m_state != UNINITIALIZED) { + return socket::make_error_code(socket::error::invalid_state); + } + + m_socket = lib::make_shared( + lib::ref(*service)); + + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl, *m_socket); + } + + m_state = READY; + + return lib::error_code(); + } + + /// Set uri hook + /** + * Called by the transport as a connection is being established to provide + * the uri being connected to to the security/socket layer. + * + * This socket policy doesn't use the uri so it is ignored. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + + /// Pre-initialize security policy + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. This method is not allowed to + * write any bytes to the wire. This initialization happens before any + * proxies or other intermediate wrappers are negotiated. + * + * @param callback Handler to call back with completion information + */ + void pre_init(init_handler callback) { + if (m_state != READY) { + callback(socket::make_error_code(socket::error::invalid_state)); + return; + } + + m_state = READING; + + callback(lib::error_code()); + } + + /// Post-initialize security policy + /** + * Called by the transport after all intermediate proxies have been + * negotiated. This gives the security policy the chance to talk with the + * real remote endpoint for a bit before the websocket handshake. + * + * @param callback Handler to call back with completion information + */ + void post_init(init_handler callback) { + callback(lib::error_code()); + } + + /// Sets the connection handle + /** + * The connection handle is passed to any handlers to identify the + * connection + * + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) { + m_hdl = hdl; + } + + /// Cancel all async operations on this socket + /** + * Attempts to cancel all async operations on this socket and reports any + * failures. + * + * NOTE: Windows XP and earlier do not support socket cancellation. + * + * @return The error that occurred, if any. + */ + lib::asio::error_code cancel_socket() { + lib::asio::error_code ec; + m_socket->cancel(ec); + return ec; + } + + void async_shutdown(socket::shutdown_handler h) { + lib::asio::error_code ec; + m_socket->shutdown(lib::asio::ip::tcp::socket::shutdown_both, ec); + h(ec); + } + + lib::error_code get_ec() const { + return lib::error_code(); + } + +public: + /// Translate any security policy specific information about an error code + /** + * Translate_ec takes an Asio error code and attempts to convert its value + * to an appropriate websocketpp error code. In the case that the Asio and + * Websocketpp error types are the same (such as using boost::asio and + * boost::system_error or using standalone asio and std::system_error the + * code will be passed through natively. + * + * In the case of a mismatch (boost::asio with std::system_error) a + * translated code will be returned. The plain socket policy does not have + * any additional information so all such errors will be reported as the + * generic transport pass_through error. + * + * @since 0.3.0 + * + * @param ec The error code to translate_ec + * @return The translated error code + */ + template + static + lib::error_code translate_ec(ErrorCodeType) { + // We don't know any more information about this error so pass through + return make_error_code(transport::error::pass_through); + } + + static + /// Overload of translate_ec to catch cases where lib::error_code is the + /// same type as lib::asio::error_code + lib::error_code translate_ec(lib::error_code ec) { + // We don't know any more information about this error, but the error is + // the same type as the one we are translating to, so pass through + // untranslated. + return ec; + } +private: + enum state { + UNINITIALIZED = 0, + READY = 1, + READING = 2 + }; + + socket_ptr m_socket; + state m_state; + + connection_hdl m_hdl; + socket_init_handler m_socket_init_handler; +}; + +/// Basic ASIO endpoint socket component +/** + * transport::asio::basic_socket::endpoint implements an endpoint socket + * component that uses Boost ASIO's ip::tcp::socket. + */ +class endpoint { +public: + /// The type of this endpoint socket component + typedef endpoint type; + + /// The type of the corresponding connection socket component + typedef connection socket_con_type; + /// The type of a shared pointer to the corresponding connection socket + /// component. + typedef socket_con_type::ptr socket_con_ptr; + + explicit endpoint() {} + + /// Checks whether the endpoint creates secure connections + /** + * @return Whether or not the endpoint creates secure connections + */ + bool is_secure() const { + return false; + } + + /// Set socket init handler + /** + * The socket init handler is called after a connection's socket is created + * but before it is used. This gives the end application an opportunity to + * set asio socket specific parameters. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } +protected: + /// Initialize a connection + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. + * + * @param scon Pointer to the socket component of the connection + * + * @return Error code (empty on success) + */ + lib::error_code init(socket_con_ptr scon) { + scon->set_socket_init_handler(m_socket_init_handler); + return lib::error_code(); + } +private: + socket_init_handler m_socket_init_handler; +}; + +} // namespace basic_socket +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/asio/security/tls.hpp b/subprojects/websocketpp/websocketpp/transport/asio/security/tls.hpp new file mode 100644 index 000000000..c76fd9aa1 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/asio/security/tls.hpp @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP +#define WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { +/// A socket policy for the asio transport that implements a TLS encrypted +/// socket by wrapping with an asio::ssl::stream +namespace tls_socket { + +/// The signature of the socket_init_handler for this socket policy +typedef lib::function&)> socket_init_handler; +/// The signature of the tls_init_handler for this socket policy +typedef lib::function(connection_hdl)> + tls_init_handler; + +/// TLS enabled Asio connection socket component +/** + * transport::asio::tls_socket::connection implements a secure connection socket + * component that uses Asio's ssl::stream to wrap an ip::tcp::socket. + */ +class connection : public lib::enable_shared_from_this { +public: + /// Type of this connection socket component + typedef connection type; + /// Type of a shared pointer to this connection socket component + typedef lib::shared_ptr ptr; + + /// Type of the ASIO socket being used + typedef lib::asio::ssl::stream socket_type; + /// Type of a shared pointer to the ASIO socket being used + typedef lib::shared_ptr socket_ptr; + /// Type of a pointer to the ASIO io_service being used + typedef lib::asio::io_service * io_service_ptr; + /// Type of a pointer to the ASIO io_service strand being used + typedef lib::shared_ptr strand_ptr; + /// Type of a shared pointer to the ASIO TLS context being used + typedef lib::shared_ptr context_ptr; + + explicit connection() { + //std::cout << "transport::asio::tls_socket::connection constructor" + // << std::endl; + } + + /// Get a shared pointer to this component + ptr get_shared() { + return shared_from_this(); + } + + /// Check whether or not this connection is secure + /** + * @return Whether or not this connection is secure + */ + bool is_secure() const { + return true; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. It can also be used to set socket options, etc + */ + socket_type::lowest_layer_type & get_raw_socket() { + return m_socket->lowest_layer(); + } + + /// Retrieve a pointer to the layer below the ssl stream + /** + * This is used internally. + */ + socket_type::next_layer_type & get_next_layer() { + return m_socket->next_layer(); + } + + /// Retrieve a pointer to the wrapped socket + /** + * This is used internally. + */ + socket_type & get_socket() { + return *m_socket; + } + + /// Set the socket initialization handler + /** + * The socket initialization handler is called after the socket object is + * created but before it is used. This gives the application a chance to + * set any ASIO socket options it needs. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } + + /// Set TLS init handler + /** + * The tls init handler is called when needed to request a TLS context for + * the library to use. A TLS init handler must be set and it must return a + * valid TLS context in order for this endpoint to be able to initialize + * TLS connections + * + * @param h The new tls_init_handler + */ + void set_tls_init_handler(tls_init_handler h) { + m_tls_init_handler = h; + } + + /// Get the remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". To indicate + * this. + * + * TODO: allow user settable remote endpoint addresses if this seems useful + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint(lib::error_code & ec) const { + std::stringstream s; + + lib::asio::error_code aec; + lib::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(aec); + + if (aec) { + ec = error::make_error_code(error::pass_through); + s << "Error getting remote endpoint: " << aec + << " (" << aec.message() << ")"; + return s.str(); + } else { + ec = lib::error_code(); + s << ep; + return s.str(); + } + } +protected: + /// Perform one time initializations + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service + * + * @param service A pointer to the endpoint's io_service + * @param strand A pointer to the connection's strand + * @param is_server Whether or not the endpoint is a server or not. + */ + lib::error_code init_asio (io_service_ptr service, strand_ptr strand, + bool is_server) + { + if (!m_tls_init_handler) { + return socket::make_error_code(socket::error::missing_tls_init_handler); + } + m_context = m_tls_init_handler(m_hdl); + + if (!m_context) { + return socket::make_error_code(socket::error::invalid_tls_context); + } + m_socket = lib::make_shared( + _WEBSOCKETPP_REF(*service),lib::ref(*m_context)); + + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl, get_socket()); + } + + m_io_service = service; + m_strand = strand; + m_is_server = is_server; + + return lib::error_code(); + } + + /// Set hostname hook + /** + * Called by the transport as a connection is being established to provide + * the hostname being connected to to the security/socket layer. + * + * This socket policy uses the hostname to set the appropriate TLS SNI + * header. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr u) { + m_uri = u; + } + + /// Pre-initialize security policy + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. This method is not allowed to + * write any bytes to the wire. This initialization happens before any + * proxies or other intermediate wrappers are negotiated. + * + * @param callback Handler to call back with completion information + */ + void pre_init(init_handler callback) { + // TODO: is this the best way to check whether this function is + // available in the version of OpenSSL being used? + // TODO: consider case where host is an IP address +#if OPENSSL_VERSION_NUMBER >= 0x90812f + if (!m_is_server) { + // For clients on systems with a suitable OpenSSL version, set the + // TLS SNI hostname header so connecting to TLS servers using SNI + // will work. + long res = SSL_set_tlsext_host_name( + get_socket().native_handle(), m_uri->get_host().c_str()); + if (!(1 == res)) { + callback(socket::make_error_code(socket::error::tls_failed_sni_hostname)); + } + } +#endif + + callback(lib::error_code()); + } + + /// Post-initialize security policy + /** + * Called by the transport after all intermediate proxies have been + * negotiated. This gives the security policy the chance to talk with the + * real remote endpoint for a bit before the websocket handshake. + * + * @param callback Handler to call back with completion information + */ + void post_init(init_handler callback) { + m_ec = socket::make_error_code(socket::error::tls_handshake_timeout); + + // TLS handshake + if (m_strand) { + m_socket->async_handshake( + get_handshake_type(), + m_strand->wrap(lib::bind( + &type::handle_init, get_shared(), + callback, + lib::placeholders::_1 + )) + ); + } else { + m_socket->async_handshake( + get_handshake_type(), + lib::bind( + &type::handle_init, get_shared(), + callback, + lib::placeholders::_1 + ) + ); + } + } + + /// Sets the connection handle + /** + * The connection handle is passed to any handlers to identify the + * connection + * + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) { + m_hdl = hdl; + } + + void handle_init(init_handler callback,lib::asio::error_code const & ec) { + if (ec) { + m_ec = socket::make_error_code(socket::error::tls_handshake_failed); + } else { + m_ec = lib::error_code(); + } + + callback(m_ec); + } + + lib::error_code get_ec() const { + return m_ec; + } + + /// Cancel all async operations on this socket + /** + * Attempts to cancel all async operations on this socket and reports any + * failures. + * + * NOTE: Windows XP and earlier do not support socket cancellation. + * + * @return The error that occurred, if any. + */ + lib::asio::error_code cancel_socket() { + lib::asio::error_code ec; + get_raw_socket().cancel(ec); + return ec; + } + + void async_shutdown(socket::shutdown_handler callback) { + if (m_strand) { + m_socket->async_shutdown(m_strand->wrap(callback)); + } else { + m_socket->async_shutdown(callback); + } + } + +public: + /// Translate any security policy specific information about an error code + /** + * Translate_ec takes an Asio error code and attempts to convert its value + * to an appropriate websocketpp error code. In the case that the Asio and + * Websocketpp error types are the same (such as using boost::asio and + * boost::system_error or using standalone asio and std::system_error the + * code will be passed through natively. + * + * In the case of a mismatch (boost::asio with std::system_error) a + * translated code will be returned. Any error that is determined to be + * related to TLS but does not have a more specific websocketpp error code + * is returned under the catch all error `tls_error`. Non-TLS related errors + * are returned as the transport generic error `pass_through` + * + * @since 0.3.0 + * + * @param ec The error code to translate_ec + * @return The translated error code + */ + template + static + lib::error_code translate_ec(ErrorCodeType ec) { + if (ec.category() == lib::asio::error::get_ssl_category()) { + // We know it is a TLS related error, but otherwise don't know more. + // Pass through as TLS generic. + return make_error_code(transport::error::tls_error); + } else { + // We don't know any more information about this error so pass + // through + return make_error_code(transport::error::pass_through); + } + } + + static + /// Overload of translate_ec to catch cases where lib::error_code is the + /// same type as lib::asio::error_code + lib::error_code translate_ec(lib::error_code ec) { + return ec; + } +private: + socket_type::handshake_type get_handshake_type() { + if (m_is_server) { + return lib::asio::ssl::stream_base::server; + } else { + return lib::asio::ssl::stream_base::client; + } + } + + io_service_ptr m_io_service; + strand_ptr m_strand; + context_ptr m_context; + socket_ptr m_socket; + uri_ptr m_uri; + bool m_is_server; + + lib::error_code m_ec; + + connection_hdl m_hdl; + socket_init_handler m_socket_init_handler; + tls_init_handler m_tls_init_handler; +}; + +/// TLS enabled Asio endpoint socket component +/** + * transport::asio::tls_socket::endpoint implements a secure endpoint socket + * component that uses Asio's ssl::stream to wrap an ip::tcp::socket. + */ +class endpoint { +public: + /// The type of this endpoint socket component + typedef endpoint type; + + /// The type of the corresponding connection socket component + typedef connection socket_con_type; + /// The type of a shared pointer to the corresponding connection socket + /// component. + typedef socket_con_type::ptr socket_con_ptr; + + explicit endpoint() {} + + /// Checks whether the endpoint creates secure connections + /** + * @return Whether or not the endpoint creates secure connections + */ + bool is_secure() const { + return true; + } + + /// Set socket init handler + /** + * The socket init handler is called after a connection's socket is created + * but before it is used. This gives the end application an opportunity to + * set asio socket specific parameters. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } + + /// Set TLS init handler + /** + * The tls init handler is called when needed to request a TLS context for + * the library to use. A TLS init handler must be set and it must return a + * valid TLS context in order for this endpoint to be able to initialize + * TLS connections + * + * @param h The new tls_init_handler + */ + void set_tls_init_handler(tls_init_handler h) { + m_tls_init_handler = h; + } +protected: + /// Initialize a connection + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. + * + * @param scon Pointer to the socket component of the connection + * + * @return Error code (empty on success) + */ + lib::error_code init(socket_con_ptr scon) { + scon->set_socket_init_handler(m_socket_init_handler); + scon->set_tls_init_handler(m_tls_init_handler); + return lib::error_code(); + } + +private: + socket_init_handler m_socket_init_handler; + tls_init_handler m_tls_init_handler; +}; + +} // namespace tls_socket +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/base/connection.hpp b/subprojects/websocketpp/websocketpp/transport/base/connection.hpp new file mode 100644 index 000000000..f76d40913 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/base/connection.hpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_BASE_CON_HPP +#define WEBSOCKETPP_TRANSPORT_BASE_CON_HPP + +#include +#include +#include +#include + +#include + +namespace websocketpp { +/// Transport policies provide network connectivity and timers +/** + * ### Connection Interface + * + * Transport connection components needs to provide: + * + * **init**\n + * `void init(init_handler handler)`\n + * Called once shortly after construction to give the policy the chance to + * perform one time initialization. When complete, the policy must call the + * supplied `init_handler` to continue setup. The handler takes one argument + * with the error code if any. If an error is returned here setup will fail and + * the connection will be aborted or terminated. + * + * WebSocket++ will call init only once. The transport must call `handler` + * exactly once. + * + * **async_read_at_least**\n + * `void async_read_at_least(size_t num_bytes, char *buf, size_t len, + * read_handler handler)`\n + * start an async read for at least num_bytes and at most len + * bytes into buf. Call handler when done with number of bytes read. + * + * WebSocket++ promises to have only one async_read_at_least in flight at a + * time. The transport must promise to only call read_handler once per async + * read. + * + * **async_write**\n + * `void async_write(const char* buf, size_t len, write_handler handler)`\n + * `void async_write(std::vector & bufs, write_handler handler)`\n + * Start a write of all of the data in buf or bufs. In second case data is + * written sequentially and in place without copying anything to a temporary + * location. + * + * Websocket++ promises to have only one async_write in flight at a time. + * The transport must promise to only call the write_handler once per async + * write + * + * **set_handle**\n + * `void set_handle(connection_hdl hdl)`\n + * Called by WebSocket++ to let this policy know the hdl to the connection. It + * may be stored for later use or ignored/discarded. This handle should be used + * if the policy adds any connection handlers. Connection handlers must be + * called with the handle as the first argument so that the handler code knows + * which connection generated the callback. + * + * **set_timer**\n + * `timer_ptr set_timer(long duration, timer_handler handler)`\n + * WebSocket++ uses the timers provided by the transport policy as the + * implementation of timers is often highly coupled with the implementation of + * the networking event loops. + * + * Transport timer support is an optional feature. A transport method may elect + * to implement a dummy timer object and have this method return an empty + * pointer. If so, all timer related features of WebSocket++ core will be + * disabled. This includes many security features designed to prevent denial of + * service attacks. Use timer-free transport policies with caution. + * + * **get_remote_endpoint**\n + * `std::string get_remote_endpoint()`\n + * retrieve address of remote endpoint + * + * **is_secure**\n + * `void is_secure()`\n + * whether or not the connection to the remote endpoint is secure + * + * **dispatch**\n + * `lib::error_code dispatch(dispatch_handler handler)`: invoke handler within + * the transport's event system if it uses one. Otherwise, this method should + * simply call `handler` immediately. + * + * **async_shutdown**\n + * `void async_shutdown(shutdown_handler handler)`\n + * Perform any cleanup necessary (if any). Call `handler` when complete. + */ +namespace transport { + +/// The type and signature of the callback passed to the init hook +typedef lib::function init_handler; + +/// The type and signature of the callback passed to the read method +typedef lib::function read_handler; + +/// The type and signature of the callback passed to the write method +typedef lib::function write_handler; + +/// The type and signature of the callback passed to the read method +typedef lib::function timer_handler; + +/// The type and signature of the callback passed to the shutdown method +typedef lib::function shutdown_handler; + +/// The type and signature of the callback passed to the interrupt method +typedef lib::function interrupt_handler; + +/// The type and signature of the callback passed to the dispatch method +typedef lib::function dispatch_handler; + +/// A simple utility buffer class +struct buffer { + buffer(char const * b, size_t l) : buf(b),len(l) {} + + char const * buf; + size_t len; +}; + +/// Generic transport related errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// underlying transport pass through + pass_through, + + /// async_read_at_least call requested more bytes than buffer can store + invalid_num_bytes, + + /// async_read called while another async_read was in progress + double_read, + + /// Operation aborted + operation_aborted, + + /// Operation not supported + operation_not_supported, + + /// End of file + eof, + + /// TLS short read + tls_short_read, + + /// Timer expired + timeout, + + /// read or write after shutdown + action_after_shutdown, + + /// Other TLS error + tls_error +}; + +class category : public lib::error_category { + public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic transport policy error"; + case pass_through: + return "Underlying Transport Error"; + case invalid_num_bytes: + return "async_read_at_least call requested more bytes than buffer can store"; + case operation_aborted: + return "The operation was aborted"; + case operation_not_supported: + return "The operation is not supported by this transport"; + case eof: + return "End of File"; + case tls_short_read: + return "TLS Short Read"; + case timeout: + return "Timer Expired"; + case action_after_shutdown: + return "A transport action was requested after shutdown"; + case tls_error: + return "Generic TLS related error"; + default: + return "Unknown"; + } + } +}; + +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace transport +} // namespace websocketpp +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_TRANSPORT_BASE_CON_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/base/endpoint.hpp b/subprojects/websocketpp/websocketpp/transport/base/endpoint.hpp new file mode 100644 index 000000000..4ed3e70fa --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/base/endpoint.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_BASE_HPP + +#include +#include + +namespace websocketpp { +/// Transport policies provide network connectivity and timers +/** + * ### Endpoint Interface + * + * Transport endpoint components needs to provide: + * + * **init**\n + * `lib::error_code init(transport_con_ptr tcon)`\n + * init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * **is_secure**\n + * `bool is_secure() const`\n + * Test whether the transport component of this endpoint is capable of secure + * connections. + * + * **async_connect**\n + * `void async_connect(transport_con_ptr tcon, uri_ptr location, + * connect_handler handler)`\n + * Initiate a connection to `location` using the given connection `tcon`. `tcon` + * is a pointer to the transport connection component of the connection. When + * complete, `handler` should be called with the the connection's + * `connection_hdl` and any error that occurred. + * + * **init_logging** + * `void init_logging(const lib::shared_ptr& a, const lib::shared_ptr& e)`\n + * Called once after construction to provide pointers to the endpoint's access + * and error loggers. These may be stored and used to log messages or ignored. + */ +namespace transport { + +/// The type and signature of the callback passed to the accept method +typedef lib::function accept_handler; + +/// The type and signature of the callback passed to the connect method +typedef lib::function connect_handler; + +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_BASE_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/iostream/base.hpp b/subprojects/websocketpp/websocketpp/transport/iostream/base.hpp new file mode 100644 index 000000000..f87839878 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/iostream/base.hpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP + +#include +#include +#include +#include + +#include + +#include +#include + +namespace websocketpp { +namespace transport { +/// Transport policy that uses STL iostream for I/O and does not support timers +namespace iostream { + +/// The type and signature of the callback used by iostream transport to write +typedef lib::function + write_handler; + +/// The type and signature of the callback used by iostream transport to perform +/// vectored writes. +/** + * If a vectored write handler is not set the standard write handler will be + * called multiple times. + */ +typedef lib::function const + & bufs)> vector_write_handler; + +/// The type and signature of the callback used by iostream transport to signal +/// a transport shutdown. +typedef lib::function shutdown_handler; + +/// iostream transport errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// async_read_at_least call requested more bytes than buffer can store + invalid_num_bytes, + + /// async_read called while another async_read was in progress + double_read, + + /// An operation that requires an output stream was attempted before + /// setting one. + output_stream_required, + + /// stream error + bad_stream +}; + +/// iostream transport error category +class category : public lib::error_category { + public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.iostream"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic iostream transport policy error"; + case invalid_num_bytes: + return "async_read_at_least call requested more bytes than buffer can store"; + case double_read: + return "Async read already in progress"; + case output_stream_required: + return "An output stream to be set before async_write can be used"; + case bad_stream: + return "A stream operation returned ios::bad"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the iostream transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Get an error code with the given value and the iostream transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace iostream +} // namespace transport +} // namespace websocketpp +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/iostream/connection.hpp b/subprojects/websocketpp/websocketpp/transport/iostream/connection.hpp new file mode 100644 index 000000000..899a3e256 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/iostream/connection.hpp @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP +#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP + +#include + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace websocketpp { +namespace transport { +namespace iostream { + +/// Empty timer class to stub out for timer functionality that iostream +/// transport doesn't support +struct timer { + void cancel() {} +}; + +template +class connection : public lib::enable_shared_from_this< connection > { +public: + /// Type of this connection transport component + typedef connection type; + /// Type of a shared pointer to this connection transport component + typedef lib::shared_ptr ptr; + + /// transport concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this transport's access logging policy + typedef typename config::alog_type alog_type; + /// Type of this transport's error logging policy + typedef typename config::elog_type elog_type; + + // Concurrency policy types + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef lib::shared_ptr timer_ptr; + + explicit connection(bool is_server, const lib::shared_ptr & alog, const lib::shared_ptr & elog) + : m_output_stream(NULL) + , m_reading(false) + , m_is_server(is_server) + , m_is_secure(false) + , m_alog(alog) + , m_elog(elog) + , m_remote_endpoint("iostream transport") + { + m_alog->write(log::alevel::devel,"iostream con transport constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return type::shared_from_this(); + } + + /// Register a std::ostream with the transport for writing output + /** + * Register a std::ostream with the transport. All future writes will be + * done to this output stream. + * + * @param o A pointer to the ostream to use for output. + */ + void register_ostream(std::ostream * o) { + // TODO: lock transport state? + scoped_lock_type lock(m_read_mutex); + m_output_stream = o; + } + + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * This transport policy doesn't use the uri so it is ignored. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + + /// Overloaded stream input operator + /** + * Attempts to read input from the given stream into the transport. Bytes + * will be extracted from the input stream to fulfill any pending reads. + * Input in this manner will only read until the current read buffer has + * been filled. Then it will signal the library to process the input. If the + * library's input handler adds a new async_read, additional bytes will be + * read, otherwise the input operation will end. + * + * When this function returns one of the following conditions is true: + * - There is no outstanding read operation + * - There are no more bytes available in the input stream + * + * You can use tellg() on the input stream to determine if all of the input + * bytes were read or not. + * + * If there is no pending read operation when the input method is called, it + * will return immediately and tellg() will not have changed. + */ + friend std::istream & operator>> (std::istream & in, type & t) { + // this serializes calls to external read. + scoped_lock_type lock(t.m_read_mutex); + + t.read(in); + + return in; + } + + /// Manual input supply (read some) + /** + * Copies bytes from buf into WebSocket++'s input buffers. Bytes will be + * copied from the supplied buffer to fulfill any pending library reads. It + * will return the number of bytes successfully processed. If there are no + * pending reads read_some will return immediately. Not all of the bytes may + * be able to be read in one call. + * + * @since 0.3.0-alpha4 + * + * @param buf Char buffer to read into the websocket + * @param len Length of buf + * @return The number of characters from buf actually read. + */ + size_t read_some(char const * buf, size_t len) { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + return this->read_some_impl(buf,len); + } + + /// Manual input supply (read all) + /** + * Similar to read_some, but continues to read until all bytes in the + * supplied buffer have been read or the connection runs out of read + * requests. + * + * This method still may not read all of the bytes in the input buffer. if + * it doesn't it indicates that the connection was most likely closed or + * is in an error state where it is no longer accepting new input. + * + * @since 0.3.0 + * + * @param buf Char buffer to read into the websocket + * @param len Length of buf + * @return The number of characters from buf actually read. + */ + size_t read_all(char const * buf, size_t len) { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + size_t total_read = 0; + size_t temp_read = 0; + + do { + temp_read = this->read_some_impl(buf+total_read,len-total_read); + total_read += temp_read; + } while (temp_read != 0 && total_read < len); + + return total_read; + } + + /// Manual input supply (DEPRECATED) + /** + * @deprecated DEPRECATED in favor of read_some() + * @see read_some() + */ + size_t readsome(char const * buf, size_t len) { + return this->read_some(buf,len); + } + + /// Signal EOF + /** + * Signals to the transport that data stream being read has reached EOF and + * that no more bytes may be read or written to/from the transport. + * + * @since 0.3.0-alpha4 + */ + void eof() { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + if (m_reading) { + complete_read(make_error_code(transport::error::eof)); + } + } + + /// Signal transport error + /** + * Signals to the transport that a fatal data stream error has occurred and + * that no more bytes may be read or written to/from the transport. + * + * @since 0.3.0-alpha4 + */ + void fatal_error() { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + if (m_reading) { + complete_read(make_error_code(transport::error::pass_through)); + } + } + + /// Set whether or not this connection is secure + /** + * The iostream transport does not provide any security features. As such + * it defaults to returning false when `is_secure` is called. However, the + * iostream transport may be used to wrap an external socket API that may + * provide secure transport. This method allows that external API to flag + * whether or not this connection is secure so that users of the WebSocket++ + * API will get more accurate information. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not this connection is secure. + */ + void set_secure(bool value) { + m_is_secure = value; + } + + /// Tests whether or not the underlying transport is secure + /** + * iostream transport will return false always because it has no information + * about the ultimate remote endpoint. This may or may not be accurate + * depending on the real source of bytes being input. The `set_secure` + * method may be used to flag connections that are secured by an external + * API + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return m_is_secure; + } + + /// Set human readable remote endpoint address + /** + * Sets the remote endpoint address returned by `get_remote_endpoint`. This + * value should be a human readable string that describes the remote + * endpoint. Typically an IP address or hostname, perhaps with a port. But + * may be something else depending on the nature of the underlying + * transport. + * + * If none is set the default is "iostream transport". + * + * @since 0.3.0-alpha4 + * + * @param value The remote endpoint address to set. + */ + void set_remote_endpoint(std::string value) { + m_remote_endpoint = value; + } + + /// Get human readable remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". The + * `set_remote_endpoint` method may be used by external network code to set + * a more accurate value. + * + * This value is used in access and error logs and is available to the end + * application for including in user facing interfaces and messages. + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint() const { + return m_remote_endpoint; + } + + /// Get the connection handle + /** + * @return The handle for this connection. + */ + connection_hdl get_handle() const { + return m_connection_hdl; + } + + /// Call back a function after a period of time. + /** + * Timers are not implemented in this transport. The timer pointer will + * always be empty. The handler will never be called. + * + * @param duration Length of time to wait in milliseconds + * @param callback The function to call back when the timer has expired + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long, timer_handler) { + return timer_ptr(); + } + + /// Sets the write handler + /** + * The write handler is called when the iostream transport receives data + * that needs to be written to the appropriate output location. This handler + * can be used in place of registering an ostream for output. + * + * The signature of the handler is + * `lib::error_code (connection_hdl, char const *, size_t)` The + * code returned will be reported and logged by the core library. + * + * See also, set_vector_write_handler, for an optional write handler that + * allows more efficient handling of multiple writes at once. + * + * @see set_vector_write_handler + * + * @since 0.5.0 + * + * @param h The handler to call when data is to be written. + */ + void set_write_handler(write_handler h) { + m_write_handler = h; + } + + /// Sets the vectored write handler + /** + * The vectored write handler is called when the iostream transport receives + * multiple chunks of data that need to be written to the appropriate output + * location. This handler can be used in conjunction with the write_handler + * in place of registering an ostream for output. + * + * The sequence of buffers represents bytes that should be written + * consecutively and it is suggested to group the buffers into as few next + * layer packets as possible. Vector write is used to allow implementations + * that support it to coalesce writes into a single TCP packet or TLS + * segment for improved efficiency. + * + * This is an optional handler. If it is not defined then multiple calls + * will be made to the standard write handler. + * + * The signature of the handler is + * `lib::error_code (connection_hdl, std::vector + * const & bufs)`. The code returned will be reported and logged by the core + * library. The `websocketpp::transport::buffer` type is a struct with two + * data members. buf (char const *) and len (size_t). + * + * @since 0.6.0 + * + * @param h The handler to call when vectored data is to be written. + */ + void set_vector_write_handler(vector_write_handler h) { + m_vector_write_handler = h; + } + + /// Sets the shutdown handler + /** + * The shutdown handler is called when the iostream transport receives a + * notification from the core library that it is finished with all read and + * write operations and that the underlying transport can be cleaned up. + * + * If you are using iostream transport with another socket library, this is + * a good time to close/shutdown the socket for this connection. + * + * The signature of the handler is `lib::error_code (connection_hdl)`. The + * code returned will be reported and logged by the core library. + * + * @since 0.5.0 + * + * @param h The handler to call on connection shutdown. + */ + void set_shutdown_handler(shutdown_handler h) { + m_shutdown_handler = h; + } +protected: + /// Initialize the connection transport + /** + * Initialize the connection's transport component. + * + * @param handler The `init_handler` to call when initialization is done + */ + void init(init_handler handler) { + m_alog->write(log::alevel::devel,"iostream connection init"); + handler(lib::error_code()); + } + + /// Initiate an async_read for at least num_bytes bytes into buf + /** + * Initiates an async_read request for at least num_bytes bytes. The input + * will be read into buf. A maximum of len bytes will be input. When the + * operation is complete, handler will be called with the status and number + * of bytes read. + * + * This method may or may not call handler from within the initial call. The + * application should be prepared to accept either. + * + * The application should never call this method a second time before it has + * been called back for the first read. If this is done, the second read + * will be called back immediately with a double_read error. + * + * If num_bytes or len are zero handler will be called back immediately + * indicating success. + * + * @param num_bytes Don't call handler until at least this many bytes have + * been read. + * @param buf The buffer to read bytes into + * @param len The size of buf. At maximum, this many bytes will be read. + * @param handler The callback to invoke when the operation is complete or + * ends in an error + */ + void async_read_at_least(size_t num_bytes, char *buf, size_t len, + read_handler handler) + { + std::stringstream s; + s << "iostream_con async_read_at_least: " << num_bytes; + m_alog->write(log::alevel::devel,s.str()); + + if (num_bytes > len) { + handler(make_error_code(error::invalid_num_bytes),size_t(0)); + return; + } + + if (m_reading == true) { + handler(make_error_code(error::double_read),size_t(0)); + return; + } + + if (num_bytes == 0 || len == 0) { + handler(lib::error_code(),size_t(0)); + return; + } + + m_buf = buf; + m_len = len; + m_bytes_needed = num_bytes; + m_read_handler = handler; + m_cursor = 0; + m_reading = true; + } + + /// Asyncronous Transport Write + /** + * Write len bytes in buf to the output method. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. Other possible errors (not exhaustive) + * output_stream_required: No output stream was registered to write to + * bad_stream: a ostream pass through error + * + * This method will attempt to write to the registered ostream first. If an + * ostream is not registered it will use the write handler. If neither are + * registered then an error is passed up to the connection. + * + * @param buf buffer to read bytes from + * @param len number of bytes to write + * @param handler Callback to invoke with operation status. + */ + void async_write(char const * buf, size_t len, transport::write_handler + handler) + { + m_alog->write(log::alevel::devel,"iostream_con async_write"); + // TODO: lock transport state? + + lib::error_code ec; + + if (m_output_stream) { + m_output_stream->write(buf,len); + + if (m_output_stream->bad()) { + ec = make_error_code(error::bad_stream); + } + } else if (m_write_handler) { + ec = m_write_handler(m_connection_hdl, buf, len); + } else { + ec = make_error_code(error::output_stream_required); + } + + handler(ec); + } + + /// Asyncronous Transport Write (scatter-gather) + /** + * Write a sequence of buffers to the output method. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. Other possible errors (not exhaustive) + * output_stream_required: No output stream was registered to write to + * bad_stream: a ostream pass through error + * + * This method will attempt to write to the registered ostream first. If an + * ostream is not registered it will use the write handler. If neither are + * registered then an error is passed up to the connection. + * + * @param bufs vector of buffers to write + * @param handler Callback to invoke with operation status. + */ + void async_write(std::vector const & bufs, transport::write_handler + handler) + { + m_alog->write(log::alevel::devel,"iostream_con async_write buffer list"); + // TODO: lock transport state? + + lib::error_code ec; + + if (m_output_stream) { + std::vector::const_iterator it; + for (it = bufs.begin(); it != bufs.end(); it++) { + m_output_stream->write((*it).buf,(*it).len); + + if (m_output_stream->bad()) { + ec = make_error_code(error::bad_stream); + break; + } + } + } else if (m_vector_write_handler) { + ec = m_vector_write_handler(m_connection_hdl, bufs); + } else if (m_write_handler) { + std::vector::const_iterator it; + for (it = bufs.begin(); it != bufs.end(); it++) { + ec = m_write_handler(m_connection_hdl, (*it).buf, (*it).len); + if (ec) {break;} + } + + } else { + ec = make_error_code(error::output_stream_required); + } + + handler(ec); + } + + /// Set Connection Handle + /** + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) { + m_connection_hdl = hdl; + } + + /// Call given handler back within the transport's event system (if present) + /** + * Invoke a callback within the transport's event system if it has one. If + * it doesn't, the handler will be invoked immediately before this function + * returns. + * + * @param handler The callback to invoke + * + * @return Whether or not the transport was able to register the handler for + * callback. + */ + lib::error_code dispatch(dispatch_handler handler) { + handler(); + return lib::error_code(); + } + + /// Perform cleanup on socket shutdown_handler + /** + * If a shutdown handler is set, call it and pass through its return error + * code. Otherwise assume there is nothing to do and pass through a success + * code. + * + * @param handler The `shutdown_handler` to call back when complete + */ + void async_shutdown(transport::shutdown_handler handler) { + lib::error_code ec; + + if (m_shutdown_handler) { + ec = m_shutdown_handler(m_connection_hdl); + } + + handler(ec); + } +private: + void read(std::istream &in) { + m_alog->write(log::alevel::devel,"iostream_con read"); + + while (in.good()) { + if (!m_reading) { + m_elog->write(log::elevel::devel,"write while not reading"); + break; + } + + in.read(m_buf+m_cursor,static_cast(m_len-m_cursor)); + + if (in.gcount() == 0) { + m_elog->write(log::elevel::devel,"read zero bytes"); + break; + } + + m_cursor += static_cast(in.gcount()); + + // TODO: error handling + if (in.bad()) { + m_reading = false; + complete_read(make_error_code(error::bad_stream)); + } + + if (m_cursor >= m_bytes_needed) { + m_reading = false; + complete_read(lib::error_code()); + } + } + } + + size_t read_some_impl(char const * buf, size_t len) { + m_alog->write(log::alevel::devel,"iostream_con read_some"); + + if (!m_reading) { + m_elog->write(log::elevel::devel,"write while not reading"); + return 0; + } + + size_t bytes_to_copy = (std::min)(len,m_len-m_cursor); + + std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor); + + m_cursor += bytes_to_copy; + + if (m_cursor >= m_bytes_needed) { + complete_read(lib::error_code()); + } + + return bytes_to_copy; + } + + /// Signal that a requested read is complete + /** + * Sets the reading flag to false and returns the handler that should be + * called back with the result of the read. The cursor position that is sent + * is whatever the value of m_cursor is. + * + * It MUST NOT be called when m_reading is false. + * it MUST be called while holding the read lock + * + * It is important to use this method rather than directly setting/calling + * m_read_handler back because this function makes sure to delete the + * locally stored handler which contains shared pointers that will otherwise + * cause circular reference based memory leaks. + * + * @param ec The error code to forward to the read handler + */ + void complete_read(lib::error_code const & ec) { + m_reading = false; + + read_handler handler = m_read_handler; + m_read_handler = read_handler(); + + handler(ec,m_cursor); + } + + // Read space (Protected by m_read_mutex) + char * m_buf; + size_t m_len; + size_t m_bytes_needed; + read_handler m_read_handler; + size_t m_cursor; + + // transport resources + std::ostream * m_output_stream; + connection_hdl m_connection_hdl; + write_handler m_write_handler; + vector_write_handler m_vector_write_handler; + shutdown_handler m_shutdown_handler; + + bool m_reading; + bool const m_is_server; + bool m_is_secure; + lib::shared_ptr m_alog; + lib::shared_ptr m_elog; + std::string m_remote_endpoint; + + // This lock ensures that only one thread can edit read data for this + // connection. This is a very coarse lock that is basically locked all the + // time. The nature of the connection is such that it cannot be + // parallelized, the locking is here to prevent intra-connection concurrency + // in order to allow inter-connection concurrency. + mutex_type m_read_mutex; +}; + + +} // namespace iostream +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/iostream/endpoint.hpp b/subprojects/websocketpp/websocketpp/transport/iostream/endpoint.hpp new file mode 100644 index 000000000..14cba7255 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/iostream/endpoint.hpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP +#define WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP + +#include +#include + +#include +#include + +#include + +#include + +namespace websocketpp { +namespace transport { +namespace iostream { + +template +class endpoint { +public: + /// Type of this endpoint transport component + typedef endpoint type; + /// Type of a pointer to this endpoint transport component + typedef lib::shared_ptr ptr; + + /// Type of this endpoint's concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this endpoint's error logging policy + typedef typename config::elog_type elog_type; + /// Type of this endpoint's access logging policy + typedef typename config::alog_type alog_type; + + /// Type of this endpoint transport component's associated connection + /// transport component. + typedef iostream::connection transport_con_type; + /// Type of a shared pointer to this endpoint transport component's + /// associated connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + // generate and manage our own io_service + explicit endpoint() : m_output_stream(NULL), m_is_secure(false) + { + //std::cout << "transport::iostream::endpoint constructor" << std::endl; + } + + /// Register a default output stream + /** + * The specified output stream will be assigned to future connections as the + * default output stream. + * + * @param o The ostream to use as the default output stream. + */ + void register_ostream(std::ostream * o) { + m_alog->write(log::alevel::devel,"register_ostream"); + m_output_stream = o; + } + + /// Set whether or not endpoint can create secure connections + /** + * The iostream transport does not provide any security features. As such + * it defaults to returning false when `is_secure` is called. However, the + * iostream transport may be used to wrap an external socket API that may + * provide secure transport. This method allows that external API to flag + * whether or not it can create secure connections so that users of the + * WebSocket++ API will get more accurate information. + * + * Setting this value only indicates whether or not the endpoint is capable + * of producing and managing secure connections. Connections produced by + * this endpoint must also be individually flagged as secure if they are. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not the endpoint can create secure connections. + */ + void set_secure(bool value) { + m_is_secure = value; + } + + /// Tests whether or not the underlying transport is secure + /** + * iostream transport will return false by default because it has no + * information about the ultimate remote endpoint. This may or may not be + * accurate depending on the real source of bytes being input. `set_secure` + * may be used by a wrapper API to correct the return value in the case that + * secure connections are in fact possible. + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return m_is_secure; + } + + /// Sets the write handler + /** + * The write handler is called when the iostream transport receives data + * that needs to be written to the appropriate output location. This handler + * can be used in place of registering an ostream for output. + * + * The signature of the handler is + * `lib::error_code (connection_hdl, char const *, size_t)` The + * code returned will be reported and logged by the core library. + * + * @since 0.5.0 + * + * @param h The handler to call on connection shutdown. + */ + void set_write_handler(write_handler h) { + m_write_handler = h; + } + + /// Sets the shutdown handler + /** + * The shutdown handler is called when the iostream transport receives a + * notification from the core library that it is finished with all read and + * write operations and that the underlying transport can be cleaned up. + * + * If you are using iostream transport with another socket library, this is + * a good time to close/shutdown the socket for this connection. + * + * The signature of the handler is lib::error_code (connection_hdl). The + * code returned will be reported and logged by the core library. + * + * @since 0.5.0 + * + * @param h The handler to call on connection shutdown. + */ + void set_shutdown_handler(shutdown_handler h) { + m_shutdown_handler = h; + } +protected: + /// Initialize logging + /** + * The loggers are located in the main endpoint class. As such, the + * transport doesn't have direct access to them. This method is called + * by the endpoint constructor to allow shared logging from the transport + * component. These are raw pointers to member variables of the endpoint. + * In particular, they cannot be used in the transport constructor as they + * haven't been constructed yet, and cannot be used in the transport + * destructor as they will have been destroyed by then. + * + * @param a A pointer to the access logger to use. + * @param e A pointer to the error logger to use. + */ + void init_logging(lib::shared_ptr a, lib::shared_ptr e) { + m_elog = e; + m_alog = a; + } + + /// Initiate a new connection + /** + * @param tcon A pointer to the transport connection component of the + * connection to connect. + * @param u A URI pointer to the URI to connect to. + * @param cb The function to call back with the results when complete. + */ + void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) { + cb(lib::error_code()); + } + + /// Initialize a connection + /** + * Init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * @param tcon A pointer to the transport portion of the connection. + * @return A status code indicating the success or failure of the operation + */ + lib::error_code init(transport_con_ptr tcon) { + tcon->register_ostream(m_output_stream); + if (m_shutdown_handler) { + tcon->set_shutdown_handler(m_shutdown_handler); + } + if (m_write_handler) { + tcon->set_write_handler(m_write_handler); + } + return lib::error_code(); + } +private: + std::ostream * m_output_stream; + shutdown_handler m_shutdown_handler; + write_handler m_write_handler; + + lib::shared_ptr m_elog; + lib::shared_ptr m_alog; + bool m_is_secure; +}; + + +} // namespace iostream +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/stub/base.hpp b/subprojects/websocketpp/websocketpp/transport/stub/base.hpp new file mode 100644 index 000000000..754981e29 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/stub/base.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP + +#include +#include + +#include + +namespace websocketpp { +namespace transport { +/// Stub transport policy that has no input or output. +namespace stub { + +/// stub transport errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// not implemented + not_implemented +}; + +/// stub transport error category +class category : public lib::error_category { + public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.stub"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic stub transport policy error"; + case not_implemented: + return "feature not implemented"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the stub transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Get an error code with the given value and the stub transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace stub +} // namespace transport +} // namespace websocketpp +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/stub/connection.hpp b/subprojects/websocketpp/websocketpp/transport/stub/connection.hpp new file mode 100644 index 000000000..5bbbf0d79 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/stub/connection.hpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_STUB_CON_HPP +#define WEBSOCKETPP_TRANSPORT_STUB_CON_HPP + +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace stub { + +/// Empty timer class to stub out for timer functionality that stub +/// transport doesn't support +struct timer { + void cancel() {} +}; + +template +class connection : public lib::enable_shared_from_this< connection > { +public: + /// Type of this connection transport component + typedef connection type; + /// Type of a shared pointer to this connection transport component + typedef lib::shared_ptr ptr; + + /// transport concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this transport's access logging policy + typedef typename config::alog_type alog_type; + /// Type of this transport's error logging policy + typedef typename config::elog_type elog_type; + + // Concurrency policy types + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef lib::shared_ptr timer_ptr; + + explicit connection(bool is_server, const lib::shared_ptr & alog, const lib::shared_ptr & elog) + : m_alog(alog), m_elog(elog) + { + m_alog->write(log::alevel::devel,"stub con transport constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return type::shared_from_this(); + } + + /// Set whether or not this connection is secure + /** + * Todo: docs + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not this connection is secure. + */ + void set_secure(bool value) {} + + /// Tests whether or not the underlying transport is secure + /** + * TODO: docs + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return false; + } + + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * Implementation is optional and can be ignored if the transport has no + * need for this information. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + + /// Set human readable remote endpoint address + /** + * Sets the remote endpoint address returned by `get_remote_endpoint`. This + * value should be a human readable string that describes the remote + * endpoint. Typically an IP address or hostname, perhaps with a port. But + * may be something else depending on the nature of the underlying + * transport. + * + * If none is set a default is returned. + * + * @since 0.3.0-alpha4 + * + * @param value The remote endpoint address to set. + */ + void set_remote_endpoint(std::string value) {} + + /// Get human readable remote endpoint address + /** + * TODO: docs + * + * This value is used in access and error logs and is available to the end + * application for including in user facing interfaces and messages. + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint() const { + return "unknown (stub transport)"; + } + + /// Get the connection handle + /** + * @return The handle for this connection. + */ + connection_hdl get_handle() const { + return connection_hdl(); + } + + /// Call back a function after a period of time. + /** + * Timers are not implemented in this transport. The timer pointer will + * always be empty. The handler will never be called. + * + * @param duration Length of time to wait in milliseconds + * @param callback The function to call back when the timer has expired + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler handler) { + return timer_ptr(); + } +protected: + /// Initialize the connection transport + /** + * Initialize the connection's transport component. + * + * @param handler The `init_handler` to call when initialization is done + */ + void init(init_handler handler) { + m_alog->write(log::alevel::devel,"stub connection init"); + handler(make_error_code(error::not_implemented)); + } + + /// Initiate an async_read for at least num_bytes bytes into buf + /** + * Initiates an async_read request for at least num_bytes bytes. The input + * will be read into buf. A maximum of len bytes will be input. When the + * operation is complete, handler will be called with the status and number + * of bytes read. + * + * This method may or may not call handler from within the initial call. The + * application should be prepared to accept either. + * + * The application should never call this method a second time before it has + * been called back for the first read. If this is done, the second read + * will be called back immediately with a double_read error. + * + * If num_bytes or len are zero handler will be called back immediately + * indicating success. + * + * @param num_bytes Don't call handler until at least this many bytes have + * been read. + * @param buf The buffer to read bytes into + * @param len The size of buf. At maximum, this many bytes will be read. + * @param handler The callback to invoke when the operation is complete or + * ends in an error + */ + void async_read_at_least(size_t num_bytes, char * buf, size_t len, + read_handler handler) + { + m_alog->write(log::alevel::devel, "stub_con async_read_at_least"); + handler(make_error_code(error::not_implemented), 0); + } + + /// Asyncronous Transport Write + /** + * Write len bytes in buf to the output stream. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. + * + * @param buf buffer to read bytes from + * @param len number of bytes to write + * @param handler Callback to invoke with operation status. + */ + void async_write(char const * buf, size_t len, write_handler handler) { + m_alog->write(log::alevel::devel,"stub_con async_write"); + handler(make_error_code(error::not_implemented)); + } + + /// Asyncronous Transport Write (scatter-gather) + /** + * Write a sequence of buffers to the output stream. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. + * + * @param bufs vector of buffers to write + * @param handler Callback to invoke with operation status. + */ + void async_write(std::vector const & bufs, write_handler handler) { + m_alog->write(log::alevel::devel,"stub_con async_write buffer list"); + handler(make_error_code(error::not_implemented)); + } + + /// Set Connection Handle + /** + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) {} + + /// Call given handler back within the transport's event system (if present) + /** + * Invoke a callback within the transport's event system if it has one. If + * it doesn't, the handler will be invoked immediately before this function + * returns. + * + * @param handler The callback to invoke + * + * @return Whether or not the transport was able to register the handler for + * callback. + */ + lib::error_code dispatch(dispatch_handler handler) { + handler(); + return lib::error_code(); + } + + /// Perform cleanup on socket shutdown_handler + /** + * @param h The `shutdown_handler` to call back when complete + */ + void async_shutdown(shutdown_handler handler) { + handler(lib::error_code()); + } +private: + // member variables! + lib::shared_ptr m_alog; + lib::shared_ptr m_elog; +}; + + +} // namespace stub +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_STUB_CON_HPP diff --git a/subprojects/websocketpp/websocketpp/transport/stub/endpoint.hpp b/subprojects/websocketpp/websocketpp/transport/stub/endpoint.hpp new file mode 100644 index 000000000..3bbb78f35 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/transport/stub/endpoint.hpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_STUB_HPP +#define WEBSOCKETPP_TRANSPORT_STUB_HPP + +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace stub { + +template +class endpoint { +public: + /// Type of this endpoint transport component + typedef endpoint type; + /// Type of a pointer to this endpoint transport component + typedef lib::shared_ptr ptr; + + /// Type of this endpoint's concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this endpoint's error logging policy + typedef typename config::elog_type elog_type; + /// Type of this endpoint's access logging policy + typedef typename config::alog_type alog_type; + + /// Type of this endpoint transport component's associated connection + /// transport component. + typedef stub::connection transport_con_type; + /// Type of a shared pointer to this endpoint transport component's + /// associated connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + // generate and manage our own io_service + explicit endpoint() + { + //std::cout << "transport::iostream::endpoint constructor" << std::endl; + } + + /// Set whether or not endpoint can create secure connections + /** + * TODO: docs + * + * Setting this value only indicates whether or not the endpoint is capable + * of producing and managing secure connections. Connections produced by + * this endpoint must also be individually flagged as secure if they are. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not the endpoint can create secure connections. + */ + void set_secure(bool value) {} + + /// Tests whether or not the underlying transport is secure + /** + * TODO: docs + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return false; + } +protected: + /// Initialize logging + /** + * The loggers are located in the main endpoint class. As such, the + * transport doesn't have direct access to them. This method is called + * by the endpoint constructor to allow shared logging from the transport + * component. These are raw pointers to member variables of the endpoint. + * In particular, they cannot be used in the transport constructor as they + * haven't been constructed yet, and cannot be used in the transport + * destructor as they will have been destroyed by then. + * + * @param a A pointer to the access logger to use. + * @param e A pointer to the error logger to use. + */ + void init_logging(alog_type * a, elog_type * e) {} + + /// Initiate a new connection + /** + * @param tcon A pointer to the transport connection component of the + * connection to connect. + * @param u A URI pointer to the URI to connect to. + * @param cb The function to call back with the results when complete. + */ + void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) { + cb(make_error_code(error::not_implemented)); + } + + /// Initialize a connection + /** + * Init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * @param tcon A pointer to the transport portion of the connection. + * @return A status code indicating the success or failure of the operation + */ + lib::error_code init(transport_con_ptr tcon) { + return make_error_code(error::not_implemented); + } +private: + +}; + +} // namespace stub +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_STUB_HPP diff --git a/subprojects/websocketpp/websocketpp/uri.hpp b/subprojects/websocketpp/websocketpp/uri.hpp new file mode 100644 index 000000000..c0b8b0cbd --- /dev/null +++ b/subprojects/websocketpp/websocketpp/uri.hpp @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_URI_HPP +#define WEBSOCKETPP_URI_HPP + +#include + +#include +#include + +#include +#include +#include + +namespace websocketpp { + +// TODO: figure out why this fixes horrible linking errors. + +/// Default port for ws:// +static uint16_t const uri_default_port = 80; +/// Default port for wss:// +static uint16_t const uri_default_secure_port = 443; + +class uri { +public: + explicit uri(std::string const & uri_string) : m_valid(false) { + std::string::const_iterator it; + std::string::const_iterator temp; + + int state = 0; + + it = uri_string.begin(); + size_t uri_len = uri_string.length(); + + if (uri_len >= 7 && std::equal(it,it+6,"wss://")) { + m_secure = true; + m_scheme = "wss"; + it += 6; + } else if (uri_len >= 6 && std::equal(it,it+5,"ws://")) { + m_secure = false; + m_scheme = "ws"; + it += 5; + } else if (uri_len >= 8 && std::equal(it,it+7,"http://")) { + m_secure = false; + m_scheme = "http"; + it += 7; + } else if (uri_len >= 9 && std::equal(it,it+8,"https://")) { + m_secure = true; + m_scheme = "https"; + it += 8; + } else { + return; + } + + // extract host. + // either a host string + // an IPv4 address + // or an IPv6 address + if (*it == '[') { + ++it; + // IPv6 literal + // extract IPv6 digits until ] + + // TODO: this doesn't work on g++... not sure why + //temp = std::find(it,it2,']'); + + temp = it; + while (temp != uri_string.end()) { + if (*temp == ']') { + break; + } + ++temp; + } + + if (temp == uri_string.end()) { + return; + } else { + // validate IPv6 literal parts + // can contain numbers, a-f and A-F + m_host.append(it,temp); + } + it = temp+1; + if (it == uri_string.end()) { + state = 2; + } else if (*it == '/') { + state = 2; + ++it; + } else if (*it == ':') { + state = 1; + ++it; + } else { + // problem + return; + } + } else { + // IPv4 or hostname + // extract until : or / + while (state == 0) { + if (it == uri_string.end()) { + state = 2; + break; + } else if (*it == '/') { + state = 2; + } else if (*it == ':') { + // end hostname start port + state = 1; + } else { + m_host += *it; + } + ++it; + } + } + + // parse port + std::string port; + while (state == 1) { + if (it == uri_string.end()) { + // state is not used after this point presently. + // this should be re-enabled if it ever is needed in a future + // refactoring + //state = 3; + break; + } else if (*it == '/') { + state = 3; + } else { + port += *it; + } + ++it; + } + + lib::error_code ec; + m_port = get_port_from_string(port, ec); + + if (ec) { + return; + } + + m_resource = "/"; + m_resource.append(it,uri_string.end()); + + + m_valid = true; + } + + uri(bool secure, std::string const & host, uint16_t port, + std::string const & resource) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port(port) + , m_secure(secure) + , m_valid(true) {} + + uri(bool secure, std::string const & host, std::string const & resource) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port(secure ? uri_default_secure_port : uri_default_port) + , m_secure(secure) + , m_valid(true) {} + + uri(bool secure, std::string const & host, std::string const & port, + std::string const & resource) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_secure(secure) + { + lib::error_code ec; + m_port = get_port_from_string(port,ec); + m_valid = !ec; + } + + uri(std::string const & scheme, std::string const & host, uint16_t port, + std::string const & resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port(port) + , m_secure(scheme == "wss" || scheme == "https") + , m_valid(true) {} + + uri(std::string scheme, std::string const & host, std::string const & resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port) + , m_secure(scheme == "wss" || scheme == "https") + , m_valid(true) {} + + uri(std::string const & scheme, std::string const & host, + std::string const & port, std::string const & resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_secure(scheme == "wss" || scheme == "https") + { + lib::error_code ec; + m_port = get_port_from_string(port,ec); + m_valid = !ec; + } + + bool get_valid() const { + return m_valid; + } + + bool get_secure() const { + return m_secure; + } + + std::string const & get_scheme() const { + return m_scheme; + } + + std::string const & get_host() const { + return m_host; + } + + std::string get_host_port() const { + if (m_port == (m_secure ? uri_default_secure_port : uri_default_port)) { + return m_host; + } else { + std::stringstream p; + p << m_host << ":" << m_port; + return p.str(); + } + } + + std::string get_authority() const { + std::stringstream p; + p << m_host << ":" << m_port; + return p.str(); + } + + uint16_t get_port() const { + return m_port; + } + + std::string get_port_str() const { + std::stringstream p; + p << m_port; + return p.str(); + } + + std::string const & get_resource() const { + return m_resource; + } + + std::string str() const { + std::stringstream s; + + s << m_scheme << "://" << m_host; + + if (m_port != (m_secure ? uri_default_secure_port : uri_default_port)) { + s << ":" << m_port; + } + + s << m_resource; + return s.str(); + } + + /// Return the query portion + /** + * Returns the query portion (after the ?) of the URI or an empty string if + * there is none. + * + * @return query portion of the URI. + */ + std::string get_query() const { + std::size_t found = m_resource.find('?'); + if (found != std::string::npos) { + return m_resource.substr(found + 1); + } else { + return ""; + } + } + + // get fragment + + // hi <3 + + // get the string representation of this URI + + //std::string base() const; // is this still needed? + + // setter methods set some or all (in the case of parse) based on the input. + // These functions throw a uri_exception on failure. + /*void set_uri(const std::string& uri); + + void set_secure(bool secure); + void set_host(const std::string& host); + void set_port(uint16_t port); + void set_port(const std::string& port); + void set_resource(const std::string& resource);*/ +private: + uint16_t get_port_from_string(std::string const & port, lib::error_code & + ec) const + { + ec = lib::error_code(); + + if (port.empty()) { + return (m_secure ? uri_default_secure_port : uri_default_port); + } + + unsigned int t_port = static_cast(atoi(port.c_str())); + + if (t_port > 65535) { + ec = error::make_error_code(error::invalid_port); + } + + if (t_port == 0) { + ec = error::make_error_code(error::invalid_port); + } + + return static_cast(t_port); + } + + std::string m_scheme; + std::string m_host; + std::string m_resource; + uint16_t m_port; + bool m_secure; + bool m_valid; +}; + +/// Pointer to a URI +typedef lib::shared_ptr uri_ptr; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_URI_HPP diff --git a/subprojects/websocketpp/websocketpp/utf8_validator.hpp b/subprojects/websocketpp/websocketpp/utf8_validator.hpp new file mode 100644 index 000000000..c057da629 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/utf8_validator.hpp @@ -0,0 +1,154 @@ +/* + * The following code is adapted from code originally written by Bjoern + * Hoehrmann . See + * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + * + * The original license: + * + * Copyright (c) 2008-2009 Bjoern Hoehrmann + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#ifndef UTF8_VALIDATOR_HPP +#define UTF8_VALIDATOR_HPP + +#include + +#include + +namespace websocketpp { +namespace utf8_validator { + +/// State that represents a valid utf8 input sequence +static unsigned int const utf8_accept = 0; +/// State that represents an invalid utf8 input sequence +static unsigned int const utf8_reject = 1; + +/// Lookup table for the UTF8 decode state machine +static uint8_t const utf8d[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +/// Decode the next byte of a UTF8 sequence +/** + * @param [out] state The decoder state to advance + * @param [out] codep The codepoint to fill in + * @param [in] byte The byte to input + * @return The ending state of the decode operation + */ +inline uint32_t decode(uint32_t * state, uint32_t * codep, uint8_t byte) { + uint32_t type = utf8d[byte]; + + *codep = (*state != utf8_accept) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state*16 + type]; + return *state; +} + +/// Provides streaming UTF8 validation functionality +class validator { +public: + /// Construct and initialize the validator + validator() : m_state(utf8_accept),m_codepoint(0) {} + + /// Advance the state of the validator with the next input byte + /** + * @param byte The byte to advance the validation state with + * @return Whether or not the byte resulted in a validation error. + */ + bool consume (uint8_t byte) { + if (utf8_validator::decode(&m_state,&m_codepoint,byte) == utf8_reject) { + return false; + } + return true; + } + + /// Advance validator state with input from an iterator pair + /** + * @param begin Input iterator to the start of the input range + * @param end Input iterator to the end of the input range + * @return Whether or not decoding the bytes resulted in a validation error. + */ + template + bool decode (iterator_type begin, iterator_type end) { + for (iterator_type it = begin; it != end; ++it) { + unsigned int result = utf8_validator::decode( + &m_state, + &m_codepoint, + static_cast(*it) + ); + + if (result == utf8_reject) { + return false; + } + } + return true; + } + + /// Return whether the input sequence ended on a valid utf8 codepoint + /** + * @return Whether or not the input sequence ended on a valid codepoint. + */ + bool complete() { + return m_state == utf8_accept; + } + + /// Reset the validator to decode another message + void reset() { + m_state = utf8_accept; + m_codepoint = 0; + } +private: + uint32_t m_state; + uint32_t m_codepoint; +}; + +/// Validate a UTF8 string +/** + * convenience function that creates a validator, validates a complete string + * and returns the result. + */ +inline bool validate(std::string const & s) { + validator v; + if (!v.decode(s.begin(),s.end())) { + return false; + } + return v.complete(); +} + +} // namespace utf8_validator +} // namespace websocketpp + +#endif // UTF8_VALIDATOR_HPP diff --git a/subprojects/websocketpp/websocketpp/utilities.hpp b/subprojects/websocketpp/websocketpp/utilities.hpp new file mode 100644 index 000000000..b983c9f3a --- /dev/null +++ b/subprojects/websocketpp/websocketpp/utilities.hpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_UTILITIES_HPP +#define WEBSOCKETPP_UTILITIES_HPP + +#include + +#include +#include +#include + +namespace websocketpp { +/// Generic non-websocket specific utility functions and data structures +namespace utility { + +/// Helper functor for case insensitive find +/** + * Based on code from + * http://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find + * + * templated version of my_equal so it could work with both char and wchar_t + */ +template +struct my_equal { + /// Construct the functor with the given locale + /** + * @param [in] loc The locale to use for determining the case of values + */ + my_equal(std::locale const & loc ) : m_loc(loc) {} + + /// Perform a case insensitive comparison + /** + * @param ch1 The first value to compare + * @param ch2 The second value to compare + * @return Whether or not the two values are equal when both are converted + * to uppercase using the given locale. + */ + bool operator()(charT ch1, charT ch2) { + return std::toupper(ch1, m_loc) == std::toupper(ch2, m_loc); + } +private: + std::locale const & m_loc; +}; + +/// Helper less than functor for case insensitive find +/** + * Based on code from + * http://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find + */ +struct ci_less { + // case-independent (ci) compare_less binary function + struct nocase_compare { + bool operator() (unsigned char const & c1, unsigned char const & c2) const { + return tolower (c1) < tolower (c2); + } + }; + bool operator() (std::string const & s1, std::string const & s2) const { + return std::lexicographical_compare + (s1.begin (), s1.end (), // source range + s2.begin (), s2.end (), // dest range + nocase_compare ()); // comparison + } +}; + +/// Find substring (case insensitive) +/** + * @param [in] haystack The string to search in + * @param [in] needle The string to search for + * @param [in] loc The locale to use for determining the case of values. + * Defaults to the current locale. + * @return An iterator to the first element of the first occurrance of needle in + * haystack. If the sequence is not found, the function returns + * haystack.end() + */ +template +typename T::const_iterator ci_find_substr(T const & haystack, T const & needle, + std::locale const & loc = std::locale()) +{ + return std::search( haystack.begin(), haystack.end(), + needle.begin(), needle.end(), my_equal(loc) ); +} + +/// Find substring (case insensitive) +/** + * @todo Is this still used? This method may not make sense.. should use + * iterators or be less generic. As is it is too tightly coupled to std::string + * + * @param [in] haystack The string to search in + * @param [in] needle The string to search for as a char array of values + * @param [in] size Length of needle + * @param [in] loc The locale to use for determining the case of values. + * Defaults to the current locale. + * @return An iterator to the first element of the first occurrance of needle in + * haystack. If the sequence is not found, the function returns + * haystack.end() + */ +template +typename 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()) +{ + return std::search( haystack.begin(), haystack.end(), + needle, needle+size, my_equal(loc) ); +} + +/// Convert a string to lowercase +/** + * @param [in] in The string to convert + * @return The converted string + */ +std::string to_lower(std::string const & in); + +/// Replace all occurrances of a substring with another +/** + * @param [in] subject The string to search in + * @param [in] search The string to search for + * @param [in] replace The string to replace with + * @return A copy of `subject` with all occurances of `search` replaced with + * `replace` + */ +std::string string_replace_all(std::string subject, std::string const & search, + std::string const & replace); + +/// Convert std::string to ascii printed string of hex digits +/** + * @param [in] input The string to print + * @return A copy of `input` converted to the printable representation of the + * hex values of its data. + */ +std::string to_hex(std::string const & input); + +/// Convert byte array (uint8_t) to ascii printed string of hex digits +/** + * @param [in] input The byte array to print + * @param [in] length The length of input + * @return A copy of `input` converted to the printable representation of the + * hex values of its data. + */ +std::string to_hex(uint8_t const * input, size_t length); + +/// Convert char array to ascii printed string of hex digits +/** + * @param [in] input The char array to print + * @param [in] length The length of input + * @return A copy of `input` converted to the printable representation of the + * hex values of its data. + */ +std::string to_hex(char const * input, size_t length); + +} // namespace utility +} // namespace websocketpp + +#include + +#endif // WEBSOCKETPP_UTILITIES_HPP diff --git a/subprojects/websocketpp/websocketpp/version.hpp b/subprojects/websocketpp/websocketpp/version.hpp new file mode 100644 index 000000000..f701ab103 --- /dev/null +++ b/subprojects/websocketpp/websocketpp/version.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_VERSION_HPP +#define WEBSOCKETPP_VERSION_HPP + +/// Namespace for the WebSocket++ project +namespace websocketpp { + +/* + other places where version information is kept + - readme.md + - changelog.md + - Doxyfile + - CMakeLists.txt +*/ + +/// Library major version number +static int const major_version = 0; +/// Library minor version number +static int const minor_version = 8; +/// Library patch version number +static int const patch_version = 1; +/// Library pre-release flag +/** + * This is a textual flag indicating the type and number for pre-release + * versions (dev, alpha, beta, rc). This will be blank for release versions. + */ + +static char const prerelease_flag[] = ""; + +/// Default user agent string +static char const user_agent[] = "WebSocket++/0.8.1"; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_VERSION_HPP diff --git a/subprojects/wswrap b/subprojects/wswrap new file mode 160000 index 000000000..47438193e --- /dev/null +++ b/subprojects/wswrap @@ -0,0 +1 @@ +Subproject commit 47438193ec50427ee28aadf294ba57baefd9f3f1