HttpConnectionManager.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/crt/Api.h>
  6. #include <aws/crt/http/HttpConnectionManager.h>
  7. #include <aws/crt/http/HttpProxyStrategy.h>
  8. #include <algorithm>
  9. #include <aws/http/connection_manager.h>
  10. namespace Aws
  11. {
  12. namespace Crt
  13. {
  14. namespace Http
  15. {
  16. struct ConnectionManagerCallbackArgs
  17. {
  18. ConnectionManagerCallbackArgs() = default;
  19. OnClientConnectionAvailable m_onClientConnectionAvailable;
  20. std::shared_ptr<HttpClientConnectionManager> m_connectionManager;
  21. };
  22. void HttpClientConnectionManager::s_shutdownCompleted(void *userData) noexcept
  23. {
  24. HttpClientConnectionManager *connectionManager =
  25. reinterpret_cast<HttpClientConnectionManager *>(userData);
  26. connectionManager->m_shutdownPromise.set_value();
  27. }
  28. HttpClientConnectionManagerOptions::HttpClientConnectionManagerOptions() noexcept
  29. : ConnectionOptions(), MaxConnections(1), EnableBlockingShutdown(false)
  30. {
  31. }
  32. std::shared_ptr<HttpClientConnectionManager> HttpClientConnectionManager::NewClientConnectionManager(
  33. const HttpClientConnectionManagerOptions &connectionManagerOptions,
  34. Allocator *allocator) noexcept
  35. {
  36. const Optional<Io::TlsConnectionOptions> &tlsOptions =
  37. connectionManagerOptions.ConnectionOptions.TlsOptions;
  38. if (tlsOptions && !(*tlsOptions))
  39. {
  40. AWS_LOGF_ERROR(
  41. AWS_LS_HTTP_GENERAL,
  42. "Cannot create HttpClientConnectionManager: ConnectionOptions contain invalid TLSOptions.");
  43. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  44. return nullptr;
  45. }
  46. const Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> &proxyOptions =
  47. connectionManagerOptions.ConnectionOptions.ProxyOptions;
  48. if (proxyOptions && proxyOptions->TlsOptions && !(*proxyOptions->TlsOptions))
  49. {
  50. AWS_LOGF_ERROR(
  51. AWS_LS_HTTP_GENERAL,
  52. "Cannot create HttpClientConnectionManager: ProxyOptions has ConnectionOptions that contain "
  53. "invalid TLSOptions.");
  54. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  55. return nullptr;
  56. }
  57. auto *toSeat = static_cast<HttpClientConnectionManager *>(
  58. aws_mem_acquire(allocator, sizeof(HttpClientConnectionManager)));
  59. if (toSeat)
  60. {
  61. toSeat = new (toSeat) HttpClientConnectionManager(connectionManagerOptions, allocator);
  62. return std::shared_ptr<HttpClientConnectionManager>(
  63. toSeat, [allocator](HttpClientConnectionManager *manager) { Delete(manager, allocator); });
  64. }
  65. return nullptr;
  66. }
  67. HttpClientConnectionManager::HttpClientConnectionManager(
  68. const HttpClientConnectionManagerOptions &options,
  69. Allocator *allocator) noexcept
  70. : m_allocator(allocator), m_connectionManager(nullptr), m_options(options), m_releaseInvoked(false)
  71. {
  72. const auto &connectionOptions = m_options.ConnectionOptions;
  73. AWS_FATAL_ASSERT(connectionOptions.HostName.size() > 0);
  74. AWS_FATAL_ASSERT(connectionOptions.Port > 0);
  75. aws_http_connection_manager_options managerOptions;
  76. AWS_ZERO_STRUCT(managerOptions);
  77. if (connectionOptions.Bootstrap != nullptr)
  78. {
  79. managerOptions.bootstrap = connectionOptions.Bootstrap->GetUnderlyingHandle();
  80. }
  81. else
  82. {
  83. managerOptions.bootstrap =
  84. ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle();
  85. }
  86. managerOptions.port = connectionOptions.Port;
  87. managerOptions.max_connections = m_options.MaxConnections;
  88. managerOptions.socket_options = &connectionOptions.SocketOptions.GetImpl();
  89. managerOptions.initial_window_size = connectionOptions.InitialWindowSize;
  90. if (options.EnableBlockingShutdown)
  91. {
  92. managerOptions.shutdown_complete_callback = s_shutdownCompleted;
  93. managerOptions.shutdown_complete_user_data = this;
  94. }
  95. else
  96. {
  97. m_shutdownPromise.set_value();
  98. }
  99. aws_http_proxy_options proxyOptions;
  100. AWS_ZERO_STRUCT(proxyOptions);
  101. if (connectionOptions.ProxyOptions)
  102. {
  103. /* This is verified by HttpClientConnectionManager::NewClientConnectionManager */
  104. AWS_FATAL_ASSERT(
  105. !connectionOptions.ProxyOptions->TlsOptions || *connectionOptions.ProxyOptions->TlsOptions);
  106. const auto &proxyOpts = connectionOptions.ProxyOptions.value();
  107. proxyOpts.InitializeRawProxyOptions(proxyOptions);
  108. managerOptions.proxy_options = &proxyOptions;
  109. }
  110. if (connectionOptions.TlsOptions)
  111. {
  112. /* This is verified by HttpClientConnectionManager::NewClientConnectionManager */
  113. AWS_FATAL_ASSERT(*connectionOptions.TlsOptions);
  114. managerOptions.tls_connection_options =
  115. const_cast<aws_tls_connection_options *>(connectionOptions.TlsOptions->GetUnderlyingHandle());
  116. }
  117. managerOptions.host = aws_byte_cursor_from_c_str(connectionOptions.HostName.c_str());
  118. m_connectionManager = aws_http_connection_manager_new(allocator, &managerOptions);
  119. }
  120. HttpClientConnectionManager::~HttpClientConnectionManager()
  121. {
  122. if (!m_releaseInvoked)
  123. {
  124. aws_http_connection_manager_release(m_connectionManager);
  125. m_shutdownPromise.get_future().get();
  126. }
  127. m_connectionManager = nullptr;
  128. }
  129. bool HttpClientConnectionManager::AcquireConnection(
  130. const OnClientConnectionAvailable &onClientConnectionAvailable) noexcept
  131. {
  132. auto connectionManagerCallbackArgs = Aws::Crt::New<ConnectionManagerCallbackArgs>(m_allocator);
  133. if (!connectionManagerCallbackArgs)
  134. {
  135. return false;
  136. }
  137. connectionManagerCallbackArgs->m_connectionManager = shared_from_this();
  138. connectionManagerCallbackArgs->m_onClientConnectionAvailable = onClientConnectionAvailable;
  139. aws_http_connection_manager_acquire_connection(
  140. m_connectionManager, s_onConnectionSetup, connectionManagerCallbackArgs);
  141. return true;
  142. }
  143. std::future<void> HttpClientConnectionManager::InitiateShutdown() noexcept
  144. {
  145. m_releaseInvoked = true;
  146. aws_http_connection_manager_release(m_connectionManager);
  147. return m_shutdownPromise.get_future();
  148. }
  149. class ManagedConnection final : public HttpClientConnection
  150. {
  151. public:
  152. ManagedConnection(
  153. aws_http_connection *connection,
  154. std::shared_ptr<HttpClientConnectionManager> connectionManager)
  155. : HttpClientConnection(connection, connectionManager->m_allocator),
  156. m_connectionManager(std::move(connectionManager))
  157. {
  158. }
  159. ~ManagedConnection() override
  160. {
  161. if (m_connection)
  162. {
  163. aws_http_connection_manager_release_connection(
  164. m_connectionManager->m_connectionManager, m_connection);
  165. m_connection = nullptr;
  166. }
  167. }
  168. private:
  169. std::shared_ptr<HttpClientConnectionManager> m_connectionManager;
  170. };
  171. void HttpClientConnectionManager::s_onConnectionSetup(
  172. aws_http_connection *connection,
  173. int errorCode,
  174. void *userData) noexcept
  175. {
  176. auto callbackArgs = static_cast<ConnectionManagerCallbackArgs *>(userData);
  177. std::shared_ptr<HttpClientConnectionManager> manager = callbackArgs->m_connectionManager;
  178. auto callback = std::move(callbackArgs->m_onClientConnectionAvailable);
  179. Delete(callbackArgs, manager->m_allocator);
  180. if (errorCode)
  181. {
  182. callback(nullptr, errorCode);
  183. return;
  184. }
  185. auto allocator = manager->m_allocator;
  186. auto connectionRawObj = Aws::Crt::New<ManagedConnection>(manager->m_allocator, connection, manager);
  187. if (!connectionRawObj)
  188. {
  189. aws_http_connection_manager_release_connection(manager->m_connectionManager, connection);
  190. callback(nullptr, AWS_ERROR_OOM);
  191. return;
  192. }
  193. auto connectionObj = std::shared_ptr<ManagedConnection>(
  194. connectionRawObj,
  195. [allocator](ManagedConnection *managedConnection) { Delete(managedConnection, allocator); });
  196. callback(connectionObj, AWS_OP_SUCCESS);
  197. }
  198. } // namespace Http
  199. } // namespace Crt
  200. } // namespace Aws