From 9c61a6df624400946532b20fa687c5b0a8654351 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 25 Sep 2013 11:24:51 -0700 Subject: [PATCH] Added AbstractHandler, WrapHandler. HTTPClient Fixes. --- Builds/VisualStudio2012/beast.vcxproj | 2 + Builds/VisualStudio2012/beast.vcxproj.filters | 246 ++--- modules/beast_asio/async/AbstractHandler.h | 706 +++++++++++++++ modules/beast_asio/async/SharedHandler.h | 12 +- modules/beast_asio/async/SharedHandlerPtr.h | 27 +- modules/beast_asio/async/SharedHandlerType.h | 1 - modules/beast_asio/async/WrapHandler.h | 205 +++++ modules/beast_asio/beast_asio.h | 11 +- modules/beast_asio/http/HTTPClientType.cpp | 852 +++++++----------- modules/beast_asio/http/HTTPClientType.h | 55 +- 10 files changed, 1432 insertions(+), 685 deletions(-) create mode 100644 modules/beast_asio/async/AbstractHandler.h create mode 100644 modules/beast_asio/async/WrapHandler.h diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj index f0916e7e2..f9fcc44d0 100644 --- a/Builds/VisualStudio2012/beast.vcxproj +++ b/Builds/VisualStudio2012/beast.vcxproj @@ -144,8 +144,10 @@ + + diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters index 7afce30be..77c34968d 100644 --- a/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Builds/VisualStudio2012/beast.vcxproj.filters @@ -38,40 +38,40 @@ _meta - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser beast_core - beast\crypto\impl\sha2 + beast_asio\async\beast\crypto\impl\sha2 - beast\crypto\impl\sha2 + beast_asio\async\beast\crypto\impl\sha2 @@ -198,15 +198,6 @@ {bf498396-2e1f-4903-be68-3053ba439af5} - - {92d1bb42-289a-4444-85c7-cb87540f2fff} - - - {8832eb52-53f9-4850-8dc9-1d579a386a0e} - - - {5904368f-a0f2-4d26-a031-8cbe4448dc3f} - {c0724499-ab69-40c3-90e2-65242dbd2eaa} @@ -216,52 +207,61 @@ {27052a76-e315-4725-9d9a-1233c7d71aba} - + + {92d1bb42-289a-4444-85c7-cb87540f2fff} + + + {8832eb52-53f9-4850-8dc9-1d579a386a0e} + + + {5904368f-a0f2-4d26-a031-8cbe4448dc3f} + + {5faa76ea-5691-4e63-8833-577f92991356} - + {93670bc9-a748-42bd-8118-8de30c468b16} - + {85158eb2-9340-4b3d-a136-f7631c7f1b7c} - + {56d34c67-7027-44ba-9f09-4591ce4afb36} - + {775ab0d6-aa5f-43d7-ab3b-3c01652a9ef1} - + {da8084c0-491b-4eb0-b750-97182a9deed4} - + {56ef157f-ad92-4da7-8fbf-00723f769732} - + {565f012b-42b7-42c9-81b7-9e93aa378000} - + {7eead15d-f9dc-4b4d-a653-57d9c090e697} - + {233e3c4d-e398-4c11-a42c-3483107eb8e9} - + {8d80e304-a42d-411a-9528-811eddff3191} - + {eabf472c-e198-409a-a65b-7c087ae911d0} - + {1fff3bd8-44ae-41df-8dd4-8bb6f07b2908} - + {9c1ef4c4-5623-4500-859f-12d6ce5ae362} - + {fc3d3f14-9ba1-43e4-b086-cbbd2f63b944} - + {44489531-f44a-439a-a6ea-d32c252b1e8b} @@ -825,28 +825,28 @@ beast_core\containers - beast\intrusive + beast_asio\async\beast\intrusive - beast\intrusive + beast_asio\async\beast\intrusive - beast\mpl + beast_asio\async\beast\mpl - beast\mpl + beast_asio\async\beast\mpl - beast\mpl + beast_asio\async\beast\mpl - beast\mpl + beast_asio\async\beast\mpl - beast\mpl + beast_asio\async\beast\mpl - beast\mpl + beast_asio\async\beast\mpl beast_core\memory @@ -972,7 +972,7 @@ beast_asio\http - beast\mpl + beast_asio\async\beast\mpl beast_asio\basics @@ -1011,64 +1011,64 @@ beast_core\memory - beast\net + beast_asio\async\beast\net - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast\type_traits + beast_asio\async\beast\type_traits - beast\type_traits + beast_asio\async\beast\type_traits - beast\type_traits + beast_asio\async\beast\type_traits - beast\type_traits + beast_asio\async\beast\type_traits - beast\utility + beast_asio\async\beast\utility - beast\utility + beast_asio\async\beast\utility - beast + beast_asio\async\beast - beast\mpl + beast_asio\async\beast\mpl - beast + beast_asio\async\beast - beast\thread + beast_asio\async\beast\thread - beast\thread + beast_asio\async\beast\thread - beast\thread + beast_asio\async\beast\thread - beast\thread + beast_asio\async\beast\thread - beast + beast_asio\async\beast beast_asio\http @@ -1080,116 +1080,122 @@ beast_asio\basics - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast\intrusive + beast_asio\async\beast\intrusive - beast\intrusive + beast_asio\async\beast\intrusive - beast\mpl + beast_asio\async\beast\mpl beast_core\thread - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast + beast_asio\async\beast - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast\strings + beast_asio\async\beast\strings - beast + beast_asio\async\beast - beast\config + beast_asio\async\beast\config - beast\config + beast_asio\async\beast\config - beast\config + beast_asio\async\beast\config - beast + beast_asio\async\beast - beast\config + beast_asio\async\beast\config - beast\config + beast_asio\async\beast\config - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast + beast_asio\async\beast - beast + beast_asio\async\beast beast_core\system - beast\http + beast_asio\async\beast\http - beast\http + beast_asio\async\beast\http - beast + beast_asio\async\beast - beast\intrusive + beast_asio\async\beast\intrusive - beast + beast_asio\async\beast - beast\crypto\impl\sha2 + beast_asio\async\beast\crypto\impl\sha2 - beast\crypto + beast_asio\async\beast\crypto + + + beast_asio\async + + + beast_asio\async @@ -1692,16 +1698,16 @@ beast_extras - beast\net\impl + beast_asio\async\beast\net\impl - beast\net + beast_asio\async\beast\net - beast\utility + beast_asio\async\beast\utility - beast\utility\impl + beast_asio\async\beast\utility\impl beast_asio\http @@ -1710,52 +1716,52 @@ beast_core\thread - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser + beast_asio\async\beast\http\impl\http-parser - beast\http\impl\http-parser\contrib + beast_asio\async\beast\http\impl\http-parser\contrib - beast\http\impl\http-parser\contrib + beast_asio\async\beast\http\impl\http-parser\contrib - beast\http + beast_asio\async\beast\http - beast\http\impl + beast_asio\async\beast\http\impl - beast\strings\impl + beast_asio\async\beast\strings\impl - beast\strings\impl + beast_asio\async\beast\strings\impl - beast\strings + beast_asio\async\beast\strings - beast\http\impl + beast_asio\async\beast\http\impl - beast\http\impl + beast_asio\async\beast\http\impl - beast\crypto\impl\sha2 + beast_asio\async\beast\crypto\impl\sha2 - beast\crypto\impl\sha2 + beast_asio\async\beast\crypto\impl\sha2 - beast\crypto\impl\sha2 + beast_asio\async\beast\crypto\impl\sha2 - beast\crypto + beast_asio\async\beast\crypto - beast\crypto\impl + beast_asio\async\beast\crypto\impl diff --git a/modules/beast_asio/async/AbstractHandler.h b/modules/beast_asio/async/AbstractHandler.h new file mode 100644 index 000000000..e732324e5 --- /dev/null +++ b/modules/beast_asio/async/AbstractHandler.h @@ -0,0 +1,706 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_ABSTRACTHANDLER_H_INCLUDED +#define BEAST_ASIO_ABSTRACTHANDLER_H_INCLUDED + +namespace beast { + +namespace detail { + +struct AbstractHandlerCallBase : SharedObject +{ + //typedef SharedFunction > invoked_type; + + typedef SharedFunction invoked_type; + + virtual void* allocate (std::size_t size) = 0; + virtual void deallocate (void* p, std::size_t size) = 0; + virtual bool is_continuation () = 0; + virtual void invoke (invoked_type& invoked) = 0; + + template + void invoke (BEAST_MOVE_ARG(Function) f) + { + invoked_type invoked (BEAST_MOVE_CAST(Function)(f) + //, AbstractHandlerAllocator(this) + ); + invoke (invoked); + } +}; + +/* +template +struct AbstractHandlerAllocator +{ + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef T const* const_pointer; + typedef T const& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + AbstractHandlerAllocator (AbstractHandler* handler) noexcept + : m_ptr (handler) + { + } + + AbstractHandlerAllocator (SharedPtr const& ptr) noexcept + : m_ptr (ptr) + { + } + + template + AbstractHandlerAllocator (AbstractHandlerAllocator const& other) + : m_ptr (other.m_ptr) + { + } + + template + struct rebind + { + typedef AbstractHandlerAllocator other; + }; + + pointer address (reference x) const + { + return &x; + } + + const_pointer address (const_reference x) const + { + return &x; + } + + pointer allocate (size_type n) const + { + size_type const bytes = n * sizeof (value_type); + return static_cast (m_ptr->allocate (bytes)); + } + + void deallocate (pointer p, size_type n) const + { + size_type const bytes = n * sizeof (value_type); + m_ptr->deallocate (p, bytes); + } + + size_type max_size () const noexcept + { + return std::numeric_limits ::max () / sizeof (value_type); + } + + void construct (pointer p, const_reference val) const + { + new ((void *)p) value_type (val); + } + + void destroy (pointer p) const + { + p->~value_type (); + } + +private: + template + friend struct AbstractHandlerAllocator; + friend class AbstractHandler; + + SharedPtr m_ptr; +}; +*/ + +} + +/** A reference counted, abstract completion handler. */ +template > +class AbstractHandler; + +//------------------------------------------------------------------------------ + +// arity 0 +template +struct AbstractHandler +{ + typedef R result_type; + struct Call : detail::AbstractHandlerCallBase + { virtual R operator() () = 0; }; + + template + struct CallType : public Call + { + typedef typename A:: template rebind >::other Allocator; + CallType (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_h (BEAST_MOVE_CAST(H)(h)), m_alloc (a) + { } + R operator()() + { return (m_h)(); } + R operator()() const + { return (m_h)(); } + void* allocate (std::size_t size) + { return boost_asio_handler_alloc_helpers::allocate(size, m_h); } + void deallocate (void* pointer, std::size_t size) + { boost_asio_handler_alloc_helpers::deallocate( pointer, size, m_h); } + bool is_continuation () +#if BEAST_ASIO_HAS_CONTINUATION_HOOKS + { return boost_asio_handler_cont_helpers::is_continuation(m_h); } +#else + { return false; } +#endif + void invoke (invoked_type& invoked) + { boost_asio_handler_invoke_helpers::invoke (invoked, m_h); } + private: + H m_h; + Allocator m_alloc; + }; + + template + AbstractHandler (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_call (new ( + typename A:: template rebind >::other (a) + .allocate (1)) CallType (BEAST_MOVE_CAST(H)(h), a)) + { } + R operator() () + { return (*m_call)(); } + R operator() () const + { return (*m_call)(); } + void* allocate (std::size_t size) const { return m_call->allocate(size); } + void deallocate (void* pointer, std::size_t size) const { m_call->deallocate(pointer,size); } + bool is_continuation () const { return m_call->is_continuation(); } + template + void invoke (Function& function) const + { + m_call->invoke(function); + } + template + void invoke (Function const& function) const + { + m_call->invoke(function); + } + +private: + SharedPtr m_call; +}; + +template +void* asio_handler_allocate (std::size_t size, + AbstractHandler * handler) +{ + return handler->allocate (size); +} + +template +void asio_handler_deallocate (void* pointer, std::size_t size, + AbstractHandler * handler) +{ + handler->deallocate (pointer, size); +} + +template +bool asio_handler_is_continuation( + AbstractHandler * handler) +{ + return handler->is_continuation(); +} + +template +void asio_handler_invoke (BEAST_MOVE_ARG(Function) function, + AbstractHandler * handler) +{ + handler->invoke (BEAST_MOVE_CAST(Function)(function)); +} + +//------------------------------------------------------------------------------ + +// arity 1 +template +struct AbstractHandler +{ + typedef R result_type; + struct Call : detail::AbstractHandlerCallBase + { virtual R operator() (P1) = 0; }; + + template + struct CallType : public Call + { + typedef typename A:: template rebind >::other Allocator; + CallType (H h, A a = A ()) + : m_h (h) + , m_alloc (a) + { + } + + R operator()(P1 p1) + { return (m_h)(p1); } + R operator()(P1 p1) const + { return (m_h)(p1); } + void* allocate (std::size_t size) + { return boost_asio_handler_alloc_helpers::allocate(size, m_h); } + void deallocate (void* pointer, std::size_t size) + { boost_asio_handler_alloc_helpers::deallocate( pointer, size, m_h); } + bool is_continuation () +#if BEAST_ASIO_HAS_CONTINUATION_HOOKS + { return boost_asio_handler_cont_helpers::is_continuation(m_h); } +#else + { return false; } +#endif + void invoke (invoked_type& invoked) + { boost_asio_handler_invoke_helpers::invoke (invoked, m_h); } + private: + H m_h; + Allocator m_alloc; + }; + + template + AbstractHandler (H h, A a = A ()) + : m_call (new ( + typename A:: template rebind >::other (a) + .allocate (1)) CallType (h, a)) + { + } + + R operator() (P1 p1) + { return (*m_call)(p1); } + R operator() (P1 p1) const + { return (*m_call)(p1); } + void* allocate (std::size_t size) const { return m_call->allocate(size); } + void deallocate (void* pointer, std::size_t size) const { m_call->deallocate(pointer,size); } + bool is_continuation () const { return m_call->is_continuation(); } + template + void invoke (Function& function) const + { + m_call->invoke(function); + } + template + void invoke (Function const& function) const + { + m_call->invoke(function); + } + +private: + SharedPtr m_call; +}; + +template +void* asio_handler_allocate (std::size_t size, + AbstractHandler * handler) +{ + return handler->allocate (size); +} + +template +void asio_handler_deallocate (void* pointer, std::size_t size, + AbstractHandler * handler) +{ + handler->deallocate (pointer, size); +} + +template +bool asio_handler_is_continuation( + AbstractHandler * handler) +{ + return handler->is_continuation(); +} + +template +void asio_handler_invoke (BEAST_MOVE_ARG(Function) function, + AbstractHandler * handler) +{ + handler->invoke (BEAST_MOVE_CAST(Function)(function)); +} + +//------------------------------------------------------------------------------ + +// arity 2 +template +struct AbstractHandler +{ + typedef R result_type; + struct Call : detail::AbstractHandlerCallBase + { virtual R operator() (P1, P2) = 0; }; + + template + struct CallType : public Call + { + typedef typename A:: template rebind >::other Allocator; + CallType (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_h (BEAST_MOVE_CAST(H)(h)), m_alloc (a) + { } + R operator()(P1 p1, P2 p2) + { return (m_h)(p1, p2); } + R operator()(P1 p1, P2 p2) const + { return (m_h)(p1, p2); } + void* allocate (std::size_t size) + { return boost_asio_handler_alloc_helpers::allocate(size, m_h); } + void deallocate (void* pointer, std::size_t size) + { boost_asio_handler_alloc_helpers::deallocate( pointer, size, m_h); } + bool is_continuation () +#if BEAST_ASIO_HAS_CONTINUATION_HOOKS + { return boost_asio_handler_cont_helpers::is_continuation(m_h); } +#else + { return false; } +#endif + void invoke (invoked_type& invoked) + { boost_asio_handler_invoke_helpers::invoke (invoked, m_h); } + private: + H m_h; + Allocator m_alloc; + }; + + template + AbstractHandler (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_call (new ( + typename A:: template rebind >::other (a) + .allocate (1)) CallType (BEAST_MOVE_CAST(H)(h), a)) + { } + R operator() (P1 p1, P2 p2) + { return (*m_call)(p1, p2); } + R operator() (P1 p1, P2 p2) const + { return (*m_call)(p1, p2); } + void* allocate (std::size_t size) const { return m_call->allocate(size); } + void deallocate (void* pointer, std::size_t size) const { m_call->deallocate(pointer,size); } + bool is_continuation () const { return m_call->is_continuation(); } + template + void invoke (Function& function) const + { + m_call->invoke(function); + } + template + void invoke (Function const& function) const + { + m_call->invoke(function); + } + +private: + SharedPtr m_call; +}; + +template +void* asio_handler_allocate (std::size_t size, + AbstractHandler * handler) +{ + return handler->allocate (size); +} + +template +void asio_handler_deallocate (void* pointer, std::size_t size, + AbstractHandler * handler) +{ + handler->deallocate (pointer, size); +} + +template +bool asio_handler_is_continuation( + AbstractHandler * handler) +{ + return handler->is_continuation(); +} + +template +void asio_handler_invoke (BEAST_MOVE_ARG(Function) function, + AbstractHandler * handler) +{ + handler->invoke (BEAST_MOVE_CAST(Function)(function)); +} + +//------------------------------------------------------------------------------ + +// arity 3 +template +struct AbstractHandler +{ + typedef R result_type; + struct Call : detail::AbstractHandlerCallBase + { virtual R operator() (P1, P2, P3) = 0; }; + + template + struct CallType : public Call + { + typedef typename A:: template rebind >::other Allocator; + CallType (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_h (BEAST_MOVE_CAST(H)(h)), m_alloc (a) + { } + R operator()(P1 p1, P2 p2, P3 p3) + { return (m_h)(p1, p2, p3); } + R operator()(P1 p1, P2 p2, P3 p3) const + { return (m_h)(p1, p2, p3); } + void* allocate (std::size_t size) + { return boost_asio_handler_alloc_helpers::allocate(size, m_h); } + void deallocate (void* pointer, std::size_t size) + { boost_asio_handler_alloc_helpers::deallocate( pointer, size, m_h); } + bool is_continuation () +#if BEAST_ASIO_HAS_CONTINUATION_HOOKS + { return boost_asio_handler_cont_helpers::is_continuation(m_h); } +#else + { return false; } +#endif + void invoke (invoked_type& invoked) + { boost_asio_handler_invoke_helpers::invoke (invoked, m_h); } + private: + H m_h; + Allocator m_alloc; + }; + + template + AbstractHandler (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_call (new ( + typename A:: template rebind >::other (a) + .allocate (1)) CallType (BEAST_MOVE_CAST(H)(h), a)) + { } + R operator() (P1 p1, P2 p2, P3 p3) + { return (*m_call)(p1, p2, p3); } + R operator() (P1 p1, P2 p2, P3 p3) const + { return (*m_call)(p1, p2, p3); } + void* allocate (std::size_t size) const { return m_call->allocate(size); } + void deallocate (void* pointer, std::size_t size) const { m_call->deallocate(pointer,size); } + bool is_continuation () const { return m_call->is_continuation(); } + template + void invoke (Function& function) const + { + m_call->invoke(function); + } + template + void invoke (Function const& function) const + { + m_call->invoke(function); + } + +private: + SharedPtr m_call; +}; + +template +void* asio_handler_allocate (std::size_t size, + AbstractHandler * handler) +{ + return handler->allocate (size); +} + +template +void asio_handler_deallocate (void* pointer, std::size_t size, + AbstractHandler * handler) +{ + handler->deallocate (pointer, size); +} + +template +bool asio_handler_is_continuation( + AbstractHandler * handler) +{ + return handler->is_continuation(); +} + +template +void asio_handler_invoke (BEAST_MOVE_ARG(Function) function, + AbstractHandler * handler) +{ + handler->invoke (BEAST_MOVE_CAST(Function)(function)); +} + +//------------------------------------------------------------------------------ + +// arity 4 +template +struct AbstractHandler +{ + typedef R result_type; + struct Call : detail::AbstractHandlerCallBase + { virtual R operator() (P1, P2, P3, P4) = 0; }; + + template + struct CallType : public Call + { + typedef typename A:: template rebind >::other Allocator; + CallType (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_h (BEAST_MOVE_CAST(H)(h)), m_alloc (a) + { } + R operator()(P1 p1, P2 p2, P3 p3, P4 p4) + { return (m_h)(p1, p2, p3, p4); } + R operator()(P1 p1, P2 p2, P3 p3, P4 p4) const + { return (m_h)(p1, p2, p3, p4); } + void* allocate (std::size_t size) + { return boost_asio_handler_alloc_helpers::allocate(size, m_h); } + void deallocate (void* pointer, std::size_t size) + { boost_asio_handler_alloc_helpers::deallocate( pointer, size, m_h); } + bool is_continuation () +#if BEAST_ASIO_HAS_CONTINUATION_HOOKS + { return boost_asio_handler_cont_helpers::is_continuation(m_h); } +#else + { return false; } +#endif + void invoke (invoked_type& invoked) + { boost_asio_handler_invoke_helpers::invoke (invoked, m_h); } + private: + H m_h; + Allocator m_alloc; + }; + + template + AbstractHandler (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_call (new ( + typename A:: template rebind >::other (a) + .allocate (1)) CallType (BEAST_MOVE_CAST(H)(h), a)) + { } + R operator() (P1 p1, P2 p2, P3 p3, P4 p4) + { return (*m_call)(p1, p2, p3, p4); } + R operator() (P1 p1, P2 p2, P3 p3, P4 p4) const + { return (*m_call)(p1, p2, p3, p4); } + void* allocate (std::size_t size) const { return m_call->allocate(size); } + void deallocate (void* pointer, std::size_t size) const { m_call->deallocate(pointer,size); } + bool is_continuation () const { return m_call->is_continuation(); } + template + void invoke (Function& function) const + { + m_call->invoke(function); + } + template + void invoke (Function const& function) const + { + m_call->invoke(function); + } + +private: + SharedPtr m_call; +}; + +template +void* asio_handler_allocate (std::size_t size, + AbstractHandler * handler) +{ + return handler->allocate (size); +} + +template +void asio_handler_deallocate (void* pointer, std::size_t size, + AbstractHandler * handler) +{ + handler->deallocate (pointer, size); +} + +template +bool asio_handler_is_continuation( + AbstractHandler * handler) +{ + return handler->is_continuation(); +} + +template +void asio_handler_invoke (BEAST_MOVE_ARG(Function) function, + AbstractHandler * handler) +{ + handler->invoke (BEAST_MOVE_CAST(Function)(function)); +} + +//------------------------------------------------------------------------------ + +// arity 5 +template +struct AbstractHandler +{ + typedef R result_type; + struct Call : detail::AbstractHandlerCallBase + { virtual R operator() (P1, P2, P3, P4, P5) = 0; }; + + template + struct CallType : public Call + { + typedef typename A:: template rebind >::other Allocator; + CallType (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_h (BEAST_MOVE_CAST(H)(h)), m_alloc (a) + { } + R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + { return (m_h)(p1, p2, p3, p4, p5); } + R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const + { return (m_h)(p1, p2, p3, p4, p5); } + void* allocate (std::size_t size) + { return boost_asio_handler_alloc_helpers::allocate(size, m_h); } + void deallocate (void* pointer, std::size_t size) + { boost_asio_handler_alloc_helpers::deallocate( pointer, size, m_h); } + bool is_continuation () +#if BEAST_ASIO_HAS_CONTINUATION_HOOKS + { return boost_asio_handler_cont_helpers::is_continuation(m_h); } +#else + { return false; } +#endif + void invoke (invoked_type& invoked) + { boost_asio_handler_invoke_helpers::invoke (invoked, m_h); } + private: + H m_h; + Allocator m_alloc; + }; + + template + AbstractHandler (BEAST_MOVE_ARG(H) h, A a = A ()) + : m_call (new ( + typename A:: template rebind >::other (a) + .allocate (1)) CallType (BEAST_MOVE_CAST(H)(h), a)) + { } + R operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + { return (*m_call)(p1, p2, p3, p4, p5); } + R operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const + { return (*m_call)(p1, p2, p3, p4, p5); } + void* allocate (std::size_t size) const { return m_call->allocate(size); } + void deallocate (void* pointer, std::size_t size) const { m_call->deallocate(pointer,size); } + bool is_continuation () const { return m_call->is_continuation(); } + template + void invoke (Function& function) const + { + m_call->invoke(function); + } + template + void invoke (Function const& function) const + { + m_call->invoke(function); + } + +private: + SharedPtr m_call; +}; + +template +void* asio_handler_allocate (std::size_t size, + AbstractHandler * handler) +{ + return handler->allocate (size); +} + +template +void asio_handler_deallocate (void* pointer, std::size_t size, + AbstractHandler * handler) +{ + handler->deallocate (pointer, size); +} + +template +bool asio_handler_is_continuation( + AbstractHandler * handler) +{ + return handler->is_continuation(); +} + +template +void asio_handler_invoke (BEAST_MOVE_ARG(Function) function, + AbstractHandler * handler) +{ + handler->invoke (BEAST_MOVE_CAST(Function)(function)); +} + +} + +#endif diff --git a/modules/beast_asio/async/SharedHandler.h b/modules/beast_asio/async/SharedHandler.h index cd6543c9f..9a1c58ed1 100644 --- a/modules/beast_asio/async/SharedHandler.h +++ b/modules/beast_asio/async/SharedHandler.h @@ -45,11 +45,9 @@ class SharedHandler : public SharedObject { protected: typedef boost::system::error_code error_code; -#if 0 - typedef boost::function invoked_type; -#else - typedef SharedFunction > invoked_type; -#endif + + typedef SharedFunction > invoked_type; SharedHandler () noexcept { } @@ -65,9 +63,6 @@ public: template void invoke (BOOST_ASIO_MOVE_ARG(Function) f) -#if 0 - ; -#else { // The allocator will hold a reference to the SharedHandler // so that we can safely destroy the function object. @@ -75,7 +70,6 @@ public: SharedHandlerAllocator (this)); invoke (invoked); } -#endif virtual void invoke (invoked_type& invoked) = 0; virtual void* allocate (std::size_t size) = 0; diff --git a/modules/beast_asio/async/SharedHandlerPtr.h b/modules/beast_asio/async/SharedHandlerPtr.h index d864ca8cc..b2ef86e44 100644 --- a/modules/beast_asio/async/SharedHandlerPtr.h +++ b/modules/beast_asio/async/SharedHandlerPtr.h @@ -43,7 +43,7 @@ public: @see isNull, isNotNull */ - inline SharedHandlerPtr () noexcept + inline SharedHandlerPtr () { } @@ -55,15 +55,14 @@ public: { } - /** Construct a reference from an existing container. - */ - inline SharedHandlerPtr (SharedHandlerPtr const& other) noexcept + /** Construct a reference from an existing container. */ + inline SharedHandlerPtr (SharedHandlerPtr const& other) : m_ptr (other.m_ptr) { } /** Assign a reference from an existing container. */ - inline SharedHandlerPtr& operator= (SharedHandlerPtr const& other) noexcept + inline SharedHandlerPtr& operator= (SharedHandlerPtr const& other) { m_ptr = other.m_ptr; return *this; @@ -73,7 +72,7 @@ public: /** Move-construct a reference from an existing container. The other container is set to a null handler. */ - inline SharedHandlerPtr (SharedHandlerPtr&& other) noexcept + inline SharedHandlerPtr (SharedHandlerPtr&& other) : m_ptr (other.m_ptr) { other.m_ptr = nullptr; @@ -82,7 +81,7 @@ public: /** Move-assign a reference from an existing container. The other container is set to a null handler. */ - inline SharedHandlerPtr& operator= (SharedHandlerPtr&& other) noexcept + inline SharedHandlerPtr& operator= (SharedHandlerPtr&& other) { m_ptr = other.m_ptr; other.m_ptr = nullptr; @@ -91,13 +90,13 @@ public: #endif /** Returns true if the handler is a null handler. */ - inline bool isNull () const noexcept + inline bool isNull () const { return m_ptr == nullptr; } /** Returns true if the handler is not a null handler. */ - inline bool isNotNull () const noexcept + inline bool isNotNull () const { return m_ptr != nullptr; } @@ -105,7 +104,7 @@ public: /** Dereference the container. This returns a reference to the underlying SharedHandler object. */ - inline SharedHandler& operator* () const noexcept + inline SharedHandler& operator* () const { return *m_ptr; } @@ -113,7 +112,7 @@ public: /** SharedHandler member access. This lets you call functions directly on the SharedHandler. */ - inline SharedHandler* operator-> () const noexcept + inline SharedHandler* operator-> () const { return m_ptr.get (); } @@ -132,7 +131,7 @@ public: @endcode */ - inline SharedHandler* get () const noexcept + inline SharedHandler* get () const { return m_ptr.get (); } @@ -144,7 +143,7 @@ public: { (*m_ptr)(); } - + /** Invoke the SharedHandler with signature void(error_code) Normally this is called by a dispatcher, you shouldn't call it directly. */ @@ -180,7 +179,7 @@ private: // template -inline void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function) f, SharedHandlerPtr* ptr) +void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function) f, SharedHandlerPtr* ptr) { boost_asio_handler_invoke_helpers:: invoke diff --git a/modules/beast_asio/async/SharedHandlerType.h b/modules/beast_asio/async/SharedHandlerType.h index 77961ce2c..7ff937962 100644 --- a/modules/beast_asio/async/SharedHandlerType.h +++ b/modules/beast_asio/async/SharedHandlerType.h @@ -21,7 +21,6 @@ #define BEAST_ASIO_ASYNC_SHAREDHANDLERTYPE_H_INCLUDED /** An instance of SharedHandler that wraps an existing Handler. - The wrapped handler will meet all the execution guarantees of the original Handler object. */ diff --git a/modules/beast_asio/async/WrapHandler.h b/modules/beast_asio/async/WrapHandler.h new file mode 100644 index 000000000..7d28934fe --- /dev/null +++ b/modules/beast_asio/async/WrapHandler.h @@ -0,0 +1,205 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_WRAPHANDLER_H_INCLUDED +#define BEAST_ASIO_WRAPHANDLER_H_INCLUDED + +namespace beast { +namespace detail { + +// Wrapper returned by wrapHandler, calls the Handler in the given Context +// +template +class WrappedHandler +{ +public: + typedef void result_type; // for result_of + + WrappedHandler (Handler& handler, Context const& context) + : m_handler (handler) + , m_context (context) + { + } + + WrappedHandler (Handler const& handler, Context const& context) + : m_handler (handler) + , m_context (context) + { + } + +#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS + WrappedHandler (WrappedHandler const& other) + : m_handler (other.m_handler) + , m_context (other.m_context) + { + } + + WrappedHandler (BEAST_MOVE_ARG(WrappedHandler) other) + : m_handler (BEAST_MOVE_CAST(Handler)(other.m_handler)) + , m_context (BEAST_MOVE_CAST(Context)(other.m_context)) + { + } +#endif + + Handler& handler() + { return m_handler; } + + Handler const& handler() const + { return m_handler; } + + Context& context() + { return m_context; } + + Context const& context() const + { return m_context; } + + void operator() () + { m_handler(); } + + void operator() () const + { m_handler(); } + + template + void operator() (P1 const& p1) + { m_handler(p1); } + + template + void operator() (P1 const& p1) const + { m_handler(p1); } + + template + void operator() (P1 const& p1, P2 const& p2) + { m_handler(p1, p2); } + + template + void operator() (P1 const& p1, P2 const& p2) const + { m_handler(p1, p2); } + + template + void operator() (P1 const& p1, P2 const& p2, P3 const& p3) + { m_handler(p1, p2, p3); } + + template + void operator() (P1 const& p1, P2 const& p2, P3 const& p3) const + { m_handler(p1, p2, p3); } + + template + void operator() + (P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4) + { m_handler(p1, p2, p3, p4); } + + template + void operator() + (P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4) const + { m_handler(p1, p2, p3, p4); } + + template + void operator() (P1 const& p1, P2 const& p2, P3 const& p3, + P4 const& p4, P5 const& p5) + { m_handler(p1, p2, p3, p4, p5); } + + template + void operator() (P1 const& p1, P2 const& p2, P3 const& p3, + P4 const& p4, P5 const& p5) const + { m_handler(p1, p2, p3, p4, p5); } + + template + void operator() (P1 const& p1, P2 const& p2, P3 const& p3, + P4 const& p4, P5 const& p5, P6 const& p6) + { m_handler(p1, p2, p3, p4, p5, p6); } + + template + void operator() (P1 const& p1, P2 const& p2, P3 const& p3, + P4 const& p4, P5 const& p5, P6 const& p6) const + { m_handler(p1, p2, p3, p4, p5, p6); } + +private: + Handler m_handler; + Context m_context; +}; + +//------------------------------------------------------------------------------ + +template +void* asio_handler_allocate (std::size_t size, + WrappedHandler * this_handler) +{ + return boost_asio_handler_alloc_helpers::allocate( + size, this_handler->context()); +} + +template +void asio_handler_deallocate (void* pointer, std::size_t size, + WrappedHandler * this_handler) +{ + boost_asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->context()); +} + +template +bool asio_handler_is_continuation( + WrappedHandler * this_handler) +{ + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +void asio_handler_invoke (Function& function, + WrappedHandler * handler) +{ + boost_asio_handler_invoke_helpers::invoke( + function, handler->context()); +} + +template +void asio_handler_invoke (Function const& function, + WrappedHandler * handler) +{ + boost_asio_handler_invoke_helpers::invoke( + function, handler->context()); +} + +} + +//------------------------------------------------------------------------------ + +/** Returns a handler that calls Handler using Context hooks. + This is useful when implementing composed asynchronous operations that + need to call their own intermediate handlers before issuing the final + completion to the original handler. +*/ +template +detail::WrappedHandler + wrapHandler ( + BEAST_MOVE_ARG(Handler) handler, + BEAST_MOVE_ARG(Context) context) +{ + return detail::WrappedHandler ( + BEAST_MOVE_CAST(Handler)(handler), + BEAST_MOVE_CAST(Context)(context)); +} + +} + +#endif diff --git a/modules/beast_asio/beast_asio.h b/modules/beast_asio/beast_asio.h index ecf7efda8..3ca6fa4f1 100644 --- a/modules/beast_asio/beast_asio.h +++ b/modules/beast_asio/beast_asio.h @@ -57,8 +57,10 @@ #include "../../beast/Utility.h" #include "../../beast/HTTP.h" -namespace beast -{ +#include "async/AbstractHandler.h" +#include "async/WrapHandler.h" + +namespace beast { // Order matters # include "async/SharedHandler.h" @@ -88,8 +90,13 @@ namespace beast # include "http/HTTPRequest.h" # include "http/HTTPResponse.h" # include "http/HTTPParser.h" + +} + #include "http/HTTPClientType.h" +namespace beast { + # include "protocol/InputParser.h" # include "protocol/HandshakeDetectLogic.h" #include "protocol/HandshakeDetectLogicPROXY.h" diff --git a/modules/beast_asio/http/HTTPClientType.cpp b/modules/beast_asio/http/HTTPClientType.cpp index c35c1a12c..5c9328140 100644 --- a/modules/beast_asio/http/HTTPClientType.cpp +++ b/modules/beast_asio/http/HTTPClientType.cpp @@ -19,120 +19,108 @@ class HTTPClientType : public HTTPClientBase, public Uncopyable { -private: - using HTTPClientBase::Listener; +public: + class Session; - typedef boost::system::error_code error_code; - - class ListenerHandler + struct State { - public: - ListenerHandler () - : m_owner (nullptr) - , m_listener (nullptr) - { - } - - ListenerHandler (HTTPClientType* owner, Listener* listener = nullptr) - : m_owner (owner) - , m_listener (listener) - { - } - - ListenerHandler (ListenerHandler const& other) - : m_owner (other.m_owner) - , m_listener (other.m_listener) - { - } - - ListenerHandler& operator= (ListenerHandler const& other) - { - m_owner = other.m_owner; - m_listener = other.m_listener; - return *this; - } - - void operator() (error_code) - { - if (m_listener != nullptr) - m_listener->onHTTPRequestComplete ( - *m_owner, m_owner->result ()); - } - - private: - HTTPClientType* m_owner; - Listener* m_listener; + List list; }; -public: + typedef SharedData SharedState; + + SharedState m_state; + Journal m_journal; + double m_timeoutSeconds; + std::size_t m_messageLimitBytes; + std::size_t m_bufferSize; + boost::asio::io_service m_io_service; + WaitableEvent m_stopped; + //-------------------------------------------------------------------------- HTTPClientType ( + Journal journal, double timeoutSeconds, std::size_t messageLimitBytes, std::size_t bufferSize) - : m_timeoutSeconds (timeoutSeconds) + : m_journal (journal) + , m_timeoutSeconds (timeoutSeconds) , m_messageLimitBytes (messageLimitBytes) , m_bufferSize (bufferSize) + , m_stopped (true, true) // manual reset, initially signaled { } ~HTTPClientType () { - m_async_op = nullptr; + cancel(); + wait(); } - Result const& result () const - { - return m_result; - } - - Result const& get (URL const& url) + result_type get (URL const& url) { + result_type result; boost::asio::io_service io_service; - async_get (io_service, nullptr, url); + async_get (io_service, url, bind ( + &HTTPClientType::handle_get, placeholders::_1, &result)); io_service.run (); - return result (); + return result; } - //-------------------------------------------------------------------------- - - void async_get (boost::asio::io_service& io_service, Listener* listener, - URL const& url) + void abstract_async_get (boost::asio::io_service& io_service, URL const& url, + AbstractHandler handler) { - async_get (io_service, url, ListenerHandler (this, listener)); - } - - // Handler signature is void(error_code) - // - template - void async_get (boost::asio::io_service& io_service, - URL const& url, - BOOST_ASIO_MOVE_ARG(Handler) handler) - { - async_get (io_service, url, newErrorHandler ( - BOOST_ASIO_MOVE_CAST(Handler)(handler))); - } - - void async_get (boost::asio::io_service& io_service, - URL const& url, SharedHandlerPtr handler) - { - // This automatically dispatches - m_async_op = new AsyncGetOp ( - *this, io_service, url, handler, - m_timeoutSeconds, m_messageLimitBytes, m_bufferSize); + new Session (*this, io_service, url, + handler, m_timeoutSeconds, m_messageLimitBytes, m_bufferSize); } void cancel () { - if (m_async_op != nullptr) - { - m_async_op->cancel (); - m_async_op = nullptr; - } + SharedState::Access state (m_state); + for (List ::iterator iter (state->list.begin()); + iter != state->list.end(); ++iter) + iter->cancel(); + } + + void wait() + { + m_stopped.wait(); + } + + //-------------------------------------------------------------------------- + + void add (Session& session) + { + SharedState::Access state (m_state); + if (state->list.empty()) + m_stopped.reset(); + state->list.push_back (session); + } + + void remove (Session& session) + { + SharedState::Access state (m_state); + state->list.erase (state->list.iterator_to (session)); + if (state->list.empty()) + m_stopped.signal(); + } + + static void handle_get (result_type const& result, result_type* dest) + { + *dest = result; + } + + Journal journal() const + { + return m_journal; + } + + boost::asio::io_service& get_io_service() + { + return m_io_service; } -private: //-------------------------------------------------------------------------- /** Helper function to get a const_buffer from a String. */ @@ -160,75 +148,83 @@ private: //-------------------------------------------------------------------------- - class AsyncGetOp : public ComposedAsyncOperation + class Session + : public SharedObject + , public AsyncObject + , public List ::Node { - private: - typedef boost::asio::ip::tcp Protocol; - typedef boost::system::error_code error_code; - - typedef Protocol::resolver resolver; - typedef resolver::query query; - typedef resolver::iterator iterator; - typedef iterator::value_type resolver_entry; - typedef Protocol::socket socket; - - //---------------------------------------------------------------------- - - enum State - { - stateStart, - stateResolveComplete, - stateConnectComplete, - stateHandshakeComplete, - stateWriteComplete, - stateShutdownComplete - }; - - //---------------------------------------------------------------------- - - struct TimerHandler : SharedHandlerPtr - { - explicit TimerHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) - { - } - void operator() (error_code const& ec) - { - m_owner->timerCompletion (ec); - } - - AsyncGetOp* m_owner; - }; - - //---------------------------------------------------------------------- - public: - AsyncGetOp (HTTPClientType& owner, - boost::asio::io_service& io_service, - URL const& url, - SharedHandlerPtr const& handler, - double timeoutSeconds, - std::size_t messageLimitBytes, - std::size_t bufferSize) - : ComposedAsyncOperation (sizeof (*this), handler) - , m_owner (owner) + typedef SharedPtr Ptr; + typedef boost::asio::ip::tcp Protocol; + typedef boost::system::error_code error_code; + typedef HTTPClientBase::error_type error_type; + typedef HTTPClientBase::value_type value_type; + typedef HTTPClientBase::result_type result_type; + + typedef Protocol::resolver resolver; + typedef Protocol::socket socket; + typedef resolver::query query; + typedef resolver::iterator iterator; + typedef iterator::value_type resolver_entry; + + HTTPClientType& m_owner; + boost::asio::io_service& m_io_service; + boost::asio::io_service::strand m_strand; + boost::asio::deadline_timer m_timer; + resolver m_resolver; + socket m_socket; + AbstractHandler m_handler; + + URL m_url; + boost::asio::ssl::context m_context; + MemoryBlock m_buffer; + HTTPParser m_parser; + std::size_t m_messageLimitBytes; + std::size_t m_bytesReceived; + + String m_get_string; + WaitableEvent m_done; + ScopedPointer m_stream; + + struct State + { + State () : complete (false) + { + } + + bool complete; + error_code error; + SharedPtr response; + }; + typedef SharedData SharedState; + SharedState m_state; + + //---------------------------------------------------------------------- + + Session (HTTPClientType& owner, + boost::asio::io_service& io_service, + URL const& url, + AbstractHandler const& handler, + double timeoutSeconds, + std::size_t messageLimitBytes, + std::size_t bufferSize) + : m_owner (owner) , m_io_service (io_service) - , m_strand (m_io_service) - , m_url (url) - , m_handler (handler) + , m_strand (io_service) , m_timer (io_service) , m_resolver (io_service) , m_socket (io_service) + , m_handler (handler) + , m_url (url) , m_context (boost::asio::ssl::context::sslv23) , m_buffer (bufferSize) , m_parser (HTTPParser::typeResponse) - , m_timer_set (false) - , m_timer_canceled (false) - , m_timer_expired (false) , m_messageLimitBytes (messageLimitBytes) , m_bytesReceived (0) { + m_owner.add (*this); + + // Configure the SSL context for certificate verification m_context.set_default_verify_paths (); m_context.set_options ( boost::asio::ssl::context::no_sslv2 | @@ -236,342 +232,163 @@ private: boost::asio::ssl::context::default_workarounds); //m_context.set_verify_mode (boost::asio::ssl::verify_peer); + // Set the timer if a timeout is requested if (timeoutSeconds > 0) { m_timer.expires_from_now ( boost::posix_time::milliseconds ( long (timeoutSeconds * 1000))); - m_timer_set = true; - ++m_io_pending; - m_timer.async_wait (TimerHandler (this)); + m_timer.async_wait (m_strand.wrap (wrapHandler ( + boost::bind (&Session::handle_timer, Ptr(this), + boost::asio::placeholders::error, + CompletionCounter(this)), m_handler))); } - // Count as pending i/o - ++m_io_pending; - m_io_service.dispatch ( - m_strand.wrap (StartHandler (this))); + // Start the operation on an io_service thread + io_service.dispatch (m_strand.wrap (wrapHandler ( + boost::bind (&Session::handle_start, Ptr(this), + CompletionCounter(this)), m_handler))); } - ~AsyncGetOp () + ~Session () { + State result; + { + SharedState::ConstAccess state (m_state); + result = *state; + } + + m_io_service.wrap (m_handler) (std::make_pair ( + result.error, result.response)); + + m_owner.remove (*this); } - // Cancel all pending I/O, if any, and block until - // there are no more completion handler calls pending. - // + //---------------------------------------------------------------------- + + // Called by the owner to cancel pending i/o. void cancel () { - cancel_timer (); - m_resolver.cancel (); - error_code ec; - m_socket.close (ec); - - m_done.wait (); - } - - private: - //---------------------------------------------------------------------- - - // Counts a pending i/o as canceled - // - void io_canceled () - { - bassert (m_io_pending.get () > 0); - if (--m_io_pending == 0) - m_done.signal (); - } - - // Cancels the deadline timer. - // - void cancel_timer () - { - // Make sure the timer was set (versus infinite timeout) - if (m_timer_set) { - // See if it was already canceled. - if (! m_timer_canceled) + SharedState::Access state (m_state); + if (! state->complete) { - m_timer_canceled = true; - error_code ec; - m_timer.cancel (ec); - - // At this point, there will either be a pending completion - // or a pending abort for the handler. If its a completion, - // they will see that the timer was canceled (since we're on - // a strand, everything is serialized). If its an abort it - // counts as a cancellation anyway. Either way, we will deduct - // one i/o from the pending i/o count. + state->complete = true; + state->error = boost::asio::error::operation_aborted; } } + + cancel_all(); } - // Called to notify the original handler the operation is complete. - // - void complete (error_code const& ec) + // Cancel all pending I/O + void cancel_all () { - // Set the error code in the result. - m_owner.m_result.error = ec; - - // Cancel the deadline timer. This ensures that - // we will not return 'timeout' to the caller later. - // - cancel_timer (); - - bassert (m_io_pending.get () > 0); - - io_canceled (); - - // We call the handler directly since we know - // we are already in the right context, and - // because we need to do some things afterwards. - // - m_handler->operator() (ec); - } - - // Called every time an async operation completes. - // The return value indicates if the handler should - // stop additional activity and return immediately. - // - bool io_complete (error_code const& ec) - { - if (m_timer_expired || - ec == boost::asio::error::operation_aborted) - { - // Timer expired, or the operation was aborted due to - // cancel, so we deduct one i/o and return immediately. - // - io_canceled (); - return true; - } - - if (ec != 0 && ec != boost::asio::error::eof) - { - // A real error happened, and the timer didn't expire, so - // notify the original handler that the operation is complete. - // - complete (ec); - return true; - } - - // Process the completion as usual. If the caller does not - // call another initiating function, it is their responsibility - // to call io_canceled() to deduce one pending i/o. - // - return false; - } - - // Called when the deadline timer expires or is canceled. - // - void timerCompletion (error_code ec) - { - bassert (m_timer_set); - - if (m_timer_canceled || ec == boost::asio::error::operation_aborted) - { - // If the cancel flag is set or the operation was aborted it - // means we canceled the timer so deduct one i/o and return. - // - io_canceled (); - return; - } - - bassert (ec == 0); - - // The timer expired, so this is a real timeout scenario. - // We want to set the error code, notify the handler, and cancel - // all other pending i/o. - // - m_timer_expired = true; - - ec = error_code (boost::asio::error::timed_out, - boost::asio::error::get_system_category ()); - - // Cancel pending name resolution + error_code ec; + m_timer.cancel (ec); m_resolver.cancel (); - - // Close the socket. This will cancel up to 2 pending i/o - m_socket.close (ec); - - // Notify the original handler of a timeout error. - // The call to complete() consumes one pending i/o, which - // we need since this function counts as one completion. - // - complete (ec); + m_socket.cancel (ec); + m_socket.shutdown (socket::shutdown_both); } - //---------------------------------------------------------------------- - - struct StartHandler : SharedHandlerPtr + // Called by a completion handler when error is not eof or aborted. + void failed (error_code ec) { - explicit StartHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) { + SharedState::Access state (m_state); + if (! state->complete) + { + state->complete = true; + state->error = ec; + state->response = nullptr; + } } - void operator() () - { - m_owner->start_complete (); - } - - AsyncGetOp* m_owner; - }; - - struct ResolveHandler : SharedHandlerPtr - { - explicit ResolveHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) - { - } - - void operator() (error_code const& ec, iterator iter) - { - m_owner->resolve_complete (ec, iter); - } - - AsyncGetOp* m_owner; - }; - - struct ConnectHandler : SharedHandlerPtr - { - explicit ConnectHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) - { - } - - void operator() (error_code const& ec) - { - m_owner->connect_complete (ec); - } - - AsyncGetOp* m_owner; - }; - - struct HandshakeHandler : SharedHandlerPtr - { - explicit HandshakeHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) - { - } - - void operator() (error_code const& ec) - { - m_owner->handshake_complete (ec); - } - - AsyncGetOp* m_owner; - }; - - struct WriteHandler : SharedHandlerPtr - { - explicit WriteHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) - { - } - - void operator() (error_code const& ec, std::size_t bytes_transferred) - { - m_owner->write_complete (ec, bytes_transferred); - } - - AsyncGetOp* m_owner; - }; - - struct ReadHandler : SharedHandlerPtr - { - explicit ReadHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) - { - } - - void operator() (error_code const& ec, std::size_t bytes_transferred) - { - m_owner->read_complete (ec, bytes_transferred); - } - - AsyncGetOp* m_owner; - }; - - struct ShutdownHandler : SharedHandlerPtr - { - explicit ShutdownHandler (AsyncGetOp* owner) - : SharedHandlerPtr (owner) - , m_owner (owner) - { - } - - void operator() (error_code const& ec) - { - m_owner->shutdown_complete (ec); - } - - AsyncGetOp* m_owner; - }; - - //---------------------------------------------------------------------- + cancel_all(); + } void async_read_some () { boost::asio::mutable_buffers_1 buf ( m_buffer.getData (), m_buffer.getSize ()); - m_stream->async_read_some (buf, - m_strand.wrap (ReadHandler (this))); - } - - // Called when the HTTP parser returns an error - void parse_error () - { - //unsigned char const http_errno (m_parser.error ()); - String const http_errmsg (m_parser.message ()); - - // VFALCO TODO put the parser error in ec - error_code ec ( - boost::system::errc::invalid_argument, - boost::system::system_category ()); - - complete (ec); - } - - // Called to create an error when the message is over the limit - error_code message_limit_error () - { - // VFALCO TODO Make a suitable error code - return error_code ( - boost::system::errc::invalid_argument, - boost::system::system_category ()); + m_stream->async_read_some (buf, m_strand.wrap ( + wrapHandler (boost::bind (&Session::handle_read, + Ptr(this), boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, + CompletionCounter(this)), m_handler))); } //---------------------------------------------------------------------- + // + // Completion handlers + // - void start_complete () + // Called when there are no more pending i/o completions + void asyncHandlersComplete() + { + } + + // Called when the operation starts + void handle_start (CompletionCounter) { query q (queryFromURL (m_url)); - m_resolver.async_resolve (q, - m_strand.wrap (ResolveHandler (this))); + + m_resolver.async_resolve (q, m_strand.wrap ( + wrapHandler (boost::bind (&Session::handle_resolve, + Ptr(this), boost::asio::placeholders::error, + boost::asio::placeholders::iterator, + CompletionCounter(this)), m_handler))); } - void resolve_complete (error_code ec, iterator iter) + // Called when the timer completes + void handle_timer (error_code ec, CompletionCounter) { - if (io_complete (ec)) + if (ec == boost::asio::error::operation_aborted) return; + if (ec != 0) + { + failed (ec); + return; + } + + failed (boost::system::errc::make_error_code ( + boost::system::errc::timed_out)); + } + + // Called when the resolver completes + void handle_resolve (error_code ec, iterator iter, CompletionCounter) + { + if (ec == boost::asio::error::operation_aborted) + return; + + if (ec != 0) + { + failed (ec); + return; + } + resolver_entry const entry (*iter); - m_socket.async_connect (entry.endpoint (), - m_strand.wrap (ConnectHandler (this))); + m_socket.async_connect (entry.endpoint (), m_strand.wrap ( + wrapHandler (boost::bind (&Session::handle_connect, + Ptr(this), boost::asio::placeholders::error, + CompletionCounter(this)), m_handler))); } - void connect_complete (error_code ec) + // Called when the connection attempt completes + void handle_connect (error_code ec, CompletionCounter) { - if (io_complete (ec)) + if (ec == boost::asio::error::operation_aborted) return; + if (ec != 0) + { + failed (ec); + return; + } + if (m_url.scheme () == "https") { typedef boost::asio::ssl::stream ssl_stream; @@ -581,154 +398,160 @@ private: boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); */ - m_stream->async_handshake ( - Socket::client, HandshakeHandler (this)); + m_stream->async_handshake (Socket::client, m_strand.wrap ( + wrapHandler (boost::bind (&Session::handle_handshake, + Ptr(this), boost::asio::placeholders::error, + CompletionCounter(this)), m_handler))); return; } m_stream = new SocketWrapper (m_socket); - handshake_complete (ec); + handle_handshake (ec, CompletionCounter(this)); } - void handshake_complete (error_code ec) + // Called when the SSL handshake completes + void handle_handshake (error_code ec, CompletionCounter) { - if (io_complete (ec)) + if (ec == boost::asio::error::operation_aborted) return; + if (ec != 0) + { + failed (ec); + return; + } + m_get_string = "GET " + m_url.path() + " HTTP/1.1\r\n" + "Host: " + m_url.host() + "\r\n" + "Accept: */*\r\n" + "Connection: close\r\n\r\n"; - boost::asio::async_write ( - *m_stream, stringBuffer (m_get_string), - m_strand.wrap (WriteHandler (this))); - ++m_io_pending; + boost::asio::async_write (*m_stream, stringBuffer ( + m_get_string), m_strand.wrap (wrapHandler ( + boost::bind (&Session::handle_write, Ptr(this), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, + CompletionCounter(this)), m_handler))); async_read_some (); } - void write_complete (error_code ec, std::size_t) + // Called when the write operation completes + void handle_write (error_code ec, std::size_t, CompletionCounter) { - if (io_complete (ec)) + if (ec == boost::asio::error::operation_aborted) return; - if (! m_stream->needs_handshake ()) + if (ec != 0) { - m_socket.shutdown (socket::shutdown_send, ec); - if (ec != 0) - return complete (ec); + failed (ec); + return; } - // deduct one i/o since we aren't issuing any new one - io_canceled (); + if (! m_stream->needs_handshake ()) + m_socket.shutdown (socket::shutdown_send, ec); } - void read_complete (error_code ec, std::size_t bytes_transferred) + void handle_read (error_code ec, + std::size_t bytes_transferred, CompletionCounter) { + if (ec == boost::asio::error::operation_aborted) + return; + + if (ec != 0) + { + failed (ec); + return; + } + m_bytesReceived += bytes_transferred; if (m_bytesReceived > m_messageLimitBytes) - ec = message_limit_error (); - - if (io_complete (ec)) + { + failed (error_code ( + boost::system::errc::invalid_argument, + boost::system::system_category ())); return; + } std::size_t const bytes_parsed (m_parser.process ( m_buffer.getData (), bytes_transferred)); if (m_parser.error ()) { - parse_error (); + failed (error_code ( + boost::system::errc::invalid_argument, + boost::system::system_category ())); return; } if (bytes_parsed != bytes_transferred) { - // VFALCO TODO put an appropriate error in ec - ec = error_code ( + failed (error_code ( boost::system::errc::invalid_argument, - boost::system::system_category ()); - return complete (ec); + boost::system::system_category ())); + return; } if (ec == boost::asio::error::eof) - { m_parser.process_eof (); - } if (m_parser.finished ()) { - m_state = stateShutdownComplete; if (m_stream->needs_handshake ()) - m_stream->async_shutdown (ShutdownHandler (this)); + { + m_stream->async_shutdown (m_strand.wrap (wrapHandler ( + boost::bind (&Session::handle_shutdown, Ptr(this), + boost::asio::placeholders::error, + CompletionCounter(this)), m_handler))); + } else - shutdown_complete (error_code ()); + { + handle_shutdown (error_code (), CompletionCounter(this)); + } return; } async_read_some (); } - void shutdown_complete (error_code ec) + void handle_shutdown (error_code ec, CompletionCounter) { - if (io_complete (ec)) + if (ec == boost::asio::error::operation_aborted) return; - m_owner.m_result.response = m_parser.response (); - if (ec == boost::asio::error::eof) - ec = error_code (); + if (ec != 0) + { + failed (ec); + return; + } - return complete (ec); + { + SharedState::Access state (m_state); + if (! state->complete) + { + state->complete = true; + state->response = m_parser.response(); + } + } + + cancel_all(); } - - private: - WaitableEvent m_done; - Atomic m_io_pending; - HTTPClientType& m_owner; - boost::asio::io_service& m_io_service; - boost::asio::io_service& m_strand; - URL m_url; - SharedHandlerPtr m_handler; - boost::asio::deadline_timer m_timer; - resolver m_resolver; - socket m_socket; - ScopedPointer m_stream; - boost::asio::ssl::context m_context; - MemoryBlock m_buffer; - State m_state; - HTTPParser m_parser; - String m_get_string; - bool m_timer_set; - bool m_timer_canceled; - bool m_timer_expired; - std::size_t m_messageLimitBytes; - std::size_t m_bytesReceived; }; - - double m_timeoutSeconds; - std::size_t m_messageLimitBytes; - std::size_t m_bufferSize; - boost::asio::io_service m_io_service; - SharedPtr m_async_op; - Result m_result; }; //------------------------------------------------------------------------------ -HTTPClientBase* HTTPClientBase::New ( +HTTPClientBase* HTTPClientBase::New (Journal journal, double timeoutSeconds, std::size_t messageLimitBytes, std::size_t bufferSize) { - ScopedPointer object (new HTTPClientType - (timeoutSeconds, messageLimitBytes, bufferSize)); - return object.release (); + return new HTTPClientType ( + journal, timeoutSeconds, messageLimitBytes, bufferSize); } //------------------------------------------------------------------------------ -class HTTPClientTests - : public UnitTest - , public HTTPClientBase::Listener +class HTTPClientTests : public UnitTest { public: typedef boost::system::error_code error_code; @@ -787,19 +610,19 @@ public: } } - void log (HTTPClientBase::Result const& result) + void log (HTTPClientBase::error_type error, HTTPClientBase::value_type const& response) { - if (result.error != 0) + if (error != 0) { logMessage (String ( - "HTTPClient error: '" + result.error.message() + "'")); + "HTTPClient error: '" + error.message() + "'")); } - else if (! result.response.empty ()) + else if (! response.empty ()) { logMessage (String ("Status: ") + - String::fromNumber (result.response->status())); - - log (*result.response); + String::fromNumber (response->status())); + + log (*response); } else { @@ -809,28 +632,31 @@ public: //-------------------------------------------------------------------------- - void onHTTPRequestComplete ( - HTTPClientBase const&, HTTPClientBase::Result const& result) + void handle_get (HTTPClientBase::result_type result) { - log (result); + log (result.first, result.second); } void testSync (String const& s, double timeoutSeconds) { ScopedPointer client ( - HTTPClientBase::New (timeoutSeconds)); + HTTPClientBase::New (Journal(), timeoutSeconds)); - log (client->get (ParsedURL (s).url ())); + HTTPClientBase::result_type const& result ( + client->get (ParsedURL (s).url ())); + + log (result.first, result.second); } void testAsync (String const& s, double timeoutSeconds) { IoServiceThread t; ScopedPointer client ( - HTTPClientBase::New (timeoutSeconds)); + HTTPClientBase::New (Journal(), timeoutSeconds)); - client->async_get (t.get_io_service (), this, - ParsedURL (s).url ()); + client->async_get (t.get_io_service (), ParsedURL (s).url (), + beast::bind (&HTTPClientTests::handle_get, this, + beast::_1)); t.start (); t.join (); diff --git a/modules/beast_asio/http/HTTPClientType.h b/modules/beast_asio/http/HTTPClientType.h index db7982ae0..894bbbc6a 100644 --- a/modules/beast_asio/http/HTTPClientType.h +++ b/modules/beast_asio/http/HTTPClientType.h @@ -20,46 +20,49 @@ #ifndef BEAST_ASIO_HTTPCLIENTTYPE_H_INCLUDED #define BEAST_ASIO_HTTPCLIENTTYPE_H_INCLUDED +#include + +namespace beast { + class HTTPClientBase { public: - struct Result - { - boost::system::error_code error; - SharedPtr response; - }; - - class Listener - { - public: - virtual void onHTTPRequestComplete ( - HTTPClientBase const& client, - Result const& result) = 0; - }; + typedef boost::system::error_code error_type; + typedef SharedPtr value_type; + typedef std::pair result_type; static HTTPClientBase* New ( + Journal journal = Journal(), double timeoutSeconds = 30, std::size_t messageLimitBytes = 256 * 1024, std::size_t bufferSize = 16 * 1024); + /** Destroy the client. + This will cancel any pending i/o and block until all completion + handlers have been called. + */ virtual ~HTTPClientBase () { } - virtual Result const& result () const = 0; + virtual result_type get (URL const& url) = 0; - virtual Result const& get ( - URL const& url) = 0; + template + void async_get (boost::asio::io_service& io_service, + URL const& url, GetHandler handler) + { + abstract_async_get (io_service, url, + AbstractHandler (handler)); + } - virtual void async_get (boost::asio::io_service& io_service, - Listener* listener, - URL const& url) = 0; + virtual void abstract_async_get (boost::asio::io_service& io_service, + URL const& url, AbstractHandler handler) = 0; - /** Cancel any pending asynchronous operations. - This must be called before destroying the container if there are - any pending asynchronous operations. This routine does nothing if - there are no pending operations. The call will block until all - pending i/o is canceled. - */ - virtual void cancel () = 0; + /** Cancel any pending asynchronous operations. */ + virtual void cancel() = 0; + + /** Block until all asynchronous i/o completes. */ + virtual void wait() = 0; }; +} + #endif