connection_manager.c 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/http/connection_manager.h>
  6. #include <aws/http/connection.h>
  7. #include <aws/http/private/connection_manager_system_vtable.h>
  8. #include <aws/http/private/connection_monitor.h>
  9. #include <aws/http/private/http_impl.h>
  10. #include <aws/http/private/proxy_impl.h>
  11. #include <aws/io/channel_bootstrap.h>
  12. #include <aws/io/event_loop.h>
  13. #include <aws/io/logging.h>
  14. #include <aws/io/socket.h>
  15. #include <aws/io/tls_channel_handler.h>
  16. #include <aws/io/uri.h>
  17. #include <aws/common/clock.h>
  18. #include <aws/common/hash_table.h>
  19. #include <aws/common/linked_list.h>
  20. #include <aws/common/mutex.h>
  21. #include <aws/common/ref_count.h>
  22. #include <aws/common/string.h>
  23. #ifdef _MSC_VER
  24. # pragma warning(disable : 4232) /* function pointer to dll symbol */
  25. #endif
  26. /*
  27. * Established connections not currently in use are tracked via this structure.
  28. */
  29. struct aws_idle_connection {
  30. struct aws_allocator *allocator;
  31. struct aws_linked_list_node node;
  32. uint64_t cull_timestamp;
  33. struct aws_http_connection *connection;
  34. };
  35. /*
  36. * System vtable to use under normal circumstances
  37. */
  38. static struct aws_http_connection_manager_system_vtable s_default_system_vtable = {
  39. .create_connection = aws_http_client_connect,
  40. .release_connection = aws_http_connection_release,
  41. .close_connection = aws_http_connection_close,
  42. .is_connection_available = aws_http_connection_new_requests_allowed,
  43. .get_monotonic_time = aws_high_res_clock_get_ticks,
  44. .is_callers_thread = aws_channel_thread_is_callers_thread,
  45. .connection_get_channel = aws_http_connection_get_channel,
  46. .connection_get_version = aws_http_connection_get_version,
  47. };
  48. const struct aws_http_connection_manager_system_vtable *g_aws_http_connection_manager_default_system_vtable_ptr =
  49. &s_default_system_vtable;
  50. bool aws_http_connection_manager_system_vtable_is_valid(const struct aws_http_connection_manager_system_vtable *table) {
  51. return table->create_connection && table->close_connection && table->release_connection &&
  52. table->is_connection_available;
  53. }
  54. enum aws_http_connection_manager_state_type { AWS_HCMST_UNINITIALIZED, AWS_HCMST_READY, AWS_HCMST_SHUTTING_DOWN };
  55. /*
  56. * AWS_HCMCT_VENDED_CONNECTION: The number of connections currently being used by external users.
  57. * AWS_HCMCT_PENDING_CONNECTIONS: The number of pending new connection requests we have outstanding to the http
  58. * layer.
  59. * AWS_HCMCT_OPEN_CONNECTION: Always equal to # of connection shutdown callbacks not yet invoked
  60. * or equivalently:
  61. *
  62. * # of connections ever created by the manager - # shutdown callbacks received
  63. */
  64. enum aws_http_connection_manager_count_type {
  65. AWS_HCMCT_VENDED_CONNECTION,
  66. AWS_HCMCT_PENDING_CONNECTIONS,
  67. AWS_HCMCT_OPEN_CONNECTION,
  68. AWS_HCMCT_COUNT,
  69. };
  70. /**
  71. * Vocabulary
  72. * Acquisition - a request by a user for a connection
  73. * Pending Acquisition - a request by a user for a new connection that has not been completed. It may be
  74. * waiting on http, a release by another user, or the manager itself.
  75. * Pending Connect - a request to the http layer for a new connection that has not been resolved yet
  76. * Vended Connection - a successfully established connection that is currently in use by something; must
  77. * be released (through the connection manager) by the user before anyone else can use it. The connection
  78. * manager does not explicitly track vended connections.
  79. * Task Set - A set of operations that should be attempted once the lock is released. A task set includes
  80. * completion callbacks (which can't fail) and connection attempts (which can fail either immediately or
  81. * asynchronously).
  82. *
  83. * Requirements/Assumptions
  84. * (1) Don't invoke user callbacks while holding the internal state lock
  85. * (2) Don't invoke downstream http calls while holding the internal state lock
  86. * (3) Only log unusual or rare events while the lock is held. Common-path logging should be while it is
  87. * not held.
  88. * (4) Don't crash or do awful things (leaking resources is ok though) if the interface contract
  89. * (ref counting + balanced acquire/release of connections) is violated by the user
  90. *
  91. * In order to fulfill (1) and (2), all side-effecting operations within the connection manager follow a pattern:
  92. *
  93. * (1) Lock
  94. * (2) Make state changes based on the operation
  95. * (3) Build a set of work (completions, connect calls, releases, self-destruction) as appropriate to the operation
  96. * (4) Unlock
  97. * (5) Execute the task set
  98. *
  99. * Asynchronous work order failures are handled in the async callback, but immediate failures require
  100. * us to relock and update the internal state. When there's an immediate connect failure, we use a
  101. * conservative policy to fail all excess (beyond the # of pending connects) acquisitions; this allows us
  102. * to avoid a possible recursive invocation (and potential failures) to connect again.
  103. *
  104. * Lifecycle
  105. * Our connection manager implementation has a reasonably complex lifecycle.
  106. *
  107. * All state around the life cycle is protected by a lock. It seemed too risky and error-prone
  108. * to try and mix an atomic ref count with the internal tracking counters we need.
  109. *
  110. * Over the course of its lifetime, a connection manager moves through two states:
  111. *
  112. * READY - connections may be acquired and released. When the external ref count for the manager
  113. * drops to zero, the manager moves to:
  114. *
  115. * TODO: Seems like connections can still be release while shutting down.
  116. * SHUTTING_DOWN - connections may no longer be acquired and released (how could they if the external
  117. * ref count was accurate?) but in case of user ref errors, we simply fail attempts to do so rather
  118. * than crash or underflow. While in this state, we wait for a set of tracking counters to all fall to zero:
  119. *
  120. * pending_connect_count - the # of unresolved calls to the http layer's connect logic
  121. * open_connection_count - the # of connections for whom the shutdown callback (from http) has not been invoked
  122. * vended_connection_count - the # of connections held by external users that haven't been released. Under correct
  123. * usage this should be zero before SHUTTING_DOWN is entered, but we attempt to handle incorrect usage gracefully.
  124. *
  125. * While all the counter fall to zero and no outlife transition, connection manager will detroy itself.
  126. *
  127. * While shutting down, as pending connects resolve, we immediately release new incoming (from http) connections
  128. *
  129. * During the transition from READY to SHUTTING_DOWN, we flush the pending acquisition queue (with failure callbacks)
  130. * and since we disallow new acquires, pending_acquisition_count should always be zero after the transition.
  131. *
  132. */
  133. struct aws_http_connection_manager {
  134. struct aws_allocator *allocator;
  135. /*
  136. * A union of external downstream dependencies (primarily global http API functions) and
  137. * internal implementation references. Selectively overridden by tests in order to
  138. * enable strong coverage of internal implementation details.
  139. */
  140. const struct aws_http_connection_manager_system_vtable *system_vtable;
  141. /*
  142. * Callback to invoke when shutdown has completed and all resources have been cleaned up.
  143. */
  144. aws_http_connection_manager_shutdown_complete_fn *shutdown_complete_callback;
  145. /*
  146. * User data to pass to the shutdown completion callback.
  147. */
  148. void *shutdown_complete_user_data;
  149. /*
  150. * Controls access to all mutable state on the connection manager
  151. */
  152. struct aws_mutex lock;
  153. /*
  154. * A manager can be in one of two states, READY or SHUTTING_DOWN. The state transition
  155. * takes place when ref_count drops to zero.
  156. */
  157. enum aws_http_connection_manager_state_type state;
  158. /*
  159. * The number of all established, idle connections. So
  160. * that we don't have compute the size of a linked list every time.
  161. * It doesn't contribute to internal refcount as AWS_HCMCT_OPEN_CONNECTION includes all idle connections as well.
  162. */
  163. size_t idle_connection_count;
  164. /*
  165. * The set of all available, ready-to-be-used connections, as aws_idle_connection structs.
  166. *
  167. * This must be a LIFO stack. When connections are released by the user, they must be added on to the back.
  168. * When we vend connections to the user, they must be removed from the back first.
  169. * In this way, the list will always be sorted from oldest (in terms of time spent idle) to newest. This means
  170. * we can always use the cull timestamp of the front connection as the next scheduled time for culling.
  171. * It also means that when we cull connections, we can quit the loop as soon as we find a connection
  172. * whose timestamp is greater than the current timestamp.
  173. */
  174. struct aws_linked_list idle_connections;
  175. /*
  176. * The set of all incomplete connection acquisition requests
  177. */
  178. struct aws_linked_list pending_acquisitions;
  179. /*
  180. * The number of all incomplete connection acquisition requests. So
  181. * that we don't have compute the size of a linked list every time.
  182. */
  183. size_t pending_acquisition_count;
  184. /*
  185. * Counts that contributes to the internal refcount.
  186. * When the value changes, s_connection_manager_internal_ref_increase/decrease needed.
  187. *
  188. * AWS_HCMCT_VENDED_CONNECTION: The number of connections currently being used by external users.
  189. * AWS_HCMCT_PENDING_CONNECTIONS: The number of pending new connection requests we have outstanding to the http
  190. * layer.
  191. * AWS_HCMCT_OPEN_CONNECTION: Always equal to # of connection shutdown callbacks not yet invoked
  192. * or equivalently:
  193. *
  194. * # of connections ever created by the manager - # shutdown callbacks received
  195. */
  196. size_t internal_ref[AWS_HCMCT_COUNT];
  197. /*
  198. * The number of established new HTTP/2 connections we have waiting for SETTINGS from the http layer
  199. * It doesn't contribute to internal refcount as AWS_HCMCT_OPEN_CONNECTION inclues all connections waiting for
  200. * settings as well.
  201. */
  202. size_t pending_settings_count;
  203. /*
  204. * All the options needed to create an http connection
  205. */
  206. struct aws_client_bootstrap *bootstrap;
  207. size_t initial_window_size;
  208. struct aws_socket_options socket_options;
  209. struct aws_tls_connection_options *tls_connection_options;
  210. struct aws_http_proxy_config *proxy_config;
  211. struct aws_http_connection_monitoring_options monitoring_options;
  212. struct aws_string *host;
  213. struct proxy_env_var_settings proxy_ev_settings;
  214. struct aws_tls_connection_options *proxy_ev_tls_options;
  215. uint16_t port;
  216. /*
  217. * HTTP/2 specific.
  218. */
  219. bool http2_prior_knowledge;
  220. struct aws_array_list *initial_settings;
  221. size_t max_closed_streams;
  222. bool http2_conn_manual_window_management;
  223. /*
  224. * The maximum number of connections this manager should ever have at once.
  225. */
  226. size_t max_connections;
  227. /*
  228. * Lifecycle tracking for the connection manager. Starts at 1.
  229. *
  230. * Once this drops to zero, the manager state transitions to shutting down
  231. *
  232. * The manager is deleted when all other tracking counters have returned to zero.
  233. *
  234. * We don't use an atomic here because the shutdown phase wants to check many different
  235. * values. You could argue that we could use a sum of everything, but we still need the
  236. * individual values for proper behavior and error checking during the ready state. Also,
  237. * a hybrid atomic/lock solution felt excessively complicated and delicate.
  238. */
  239. size_t external_ref_count;
  240. /*
  241. * Internal refcount that keeps connection manager alive.
  242. *
  243. * It's a sum of all internal_ref, the `struct aws_connection_management_transaction` alive and one for any external
  244. * usage.
  245. *
  246. * Once this refcount drops to zero, connection manager should either be cleaned up all the memory all waiting for
  247. * the last task to clean un the memory and do nothing else.
  248. */
  249. struct aws_ref_count internal_ref_count;
  250. /*
  251. * if set to true, read back pressure mechanism will be enabled.
  252. */
  253. bool enable_read_back_pressure;
  254. /**
  255. * If set to a non-zero value, then connections that stay in the pool longer than the specified
  256. * timeout will be closed automatically.
  257. */
  258. uint64_t max_connection_idle_in_milliseconds;
  259. /*
  260. * Task to cull idle connections. This task is run periodically on the cull_event_loop if a non-zero
  261. * culling time interval is specified.
  262. */
  263. struct aws_task *cull_task;
  264. struct aws_event_loop *cull_event_loop;
  265. };
  266. struct aws_http_connection_manager_snapshot {
  267. enum aws_http_connection_manager_state_type state;
  268. size_t idle_connection_count;
  269. size_t pending_acquisition_count;
  270. size_t pending_settings_count;
  271. /* From internal_ref */
  272. size_t pending_connects_count;
  273. size_t vended_connection_count;
  274. size_t open_connection_count;
  275. size_t external_ref_count;
  276. };
  277. /*
  278. * Correct usage requires AWS_ZERO_STRUCT to have been called beforehand.
  279. */
  280. static void s_aws_http_connection_manager_get_snapshot(
  281. struct aws_http_connection_manager *manager,
  282. struct aws_http_connection_manager_snapshot *snapshot) {
  283. snapshot->state = manager->state;
  284. snapshot->idle_connection_count = manager->idle_connection_count;
  285. snapshot->pending_acquisition_count = manager->pending_acquisition_count;
  286. snapshot->pending_settings_count = manager->pending_settings_count;
  287. snapshot->pending_connects_count = manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS];
  288. snapshot->vended_connection_count = manager->internal_ref[AWS_HCMCT_VENDED_CONNECTION];
  289. snapshot->open_connection_count = manager->internal_ref[AWS_HCMCT_OPEN_CONNECTION];
  290. snapshot->external_ref_count = manager->external_ref_count;
  291. }
  292. static void s_aws_http_connection_manager_log_snapshot(
  293. struct aws_http_connection_manager *manager,
  294. struct aws_http_connection_manager_snapshot *snapshot) {
  295. if (snapshot->state != AWS_HCMST_UNINITIALIZED) {
  296. AWS_LOGF_DEBUG(
  297. AWS_LS_HTTP_CONNECTION_MANAGER,
  298. "id=%p: snapshot - state=%d, idle_connection_count=%zu, pending_acquire_count=%zu, "
  299. "pending_settings_count=%zu, pending_connect_count=%zu, vended_connection_count=%zu, "
  300. "open_connection_count=%zu, ref_count=%zu",
  301. (void *)manager,
  302. (int)snapshot->state,
  303. snapshot->idle_connection_count,
  304. snapshot->pending_acquisition_count,
  305. snapshot->pending_settings_count,
  306. snapshot->pending_connects_count,
  307. snapshot->vended_connection_count,
  308. snapshot->open_connection_count,
  309. snapshot->external_ref_count);
  310. } else {
  311. AWS_LOGF_DEBUG(
  312. AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: snapshot not initialized by control flow", (void *)manager);
  313. }
  314. }
  315. void aws_http_connection_manager_set_system_vtable(
  316. struct aws_http_connection_manager *manager,
  317. const struct aws_http_connection_manager_system_vtable *system_vtable) {
  318. AWS_FATAL_ASSERT(aws_http_connection_manager_system_vtable_is_valid(system_vtable));
  319. manager->system_vtable = system_vtable;
  320. }
  321. /*
  322. * A struct that functions as both the pending acquisition tracker and the about-to-complete data.
  323. *
  324. * The list in the connection manager (pending_acquisitions) is the set of all acquisition requests that we
  325. * haven't yet resolved.
  326. *
  327. * In order to make sure we never invoke callbacks while holding the manager's lock, in a number of places
  328. * we build a list of one or more acquisitions to complete. Once the lock is released
  329. * we complete all the acquisitions in the list using the data within the struct (hence why we have
  330. * "result-oriented" members like connection and error_code). This means we can fail an acquisition
  331. * simply by setting the error_code and moving it to the current transaction's completion list.
  332. */
  333. struct aws_http_connection_acquisition {
  334. struct aws_allocator *allocator;
  335. struct aws_linked_list_node node;
  336. struct aws_http_connection_manager *manager; /* Only used by logging */
  337. aws_http_connection_manager_on_connection_setup_fn *callback;
  338. void *user_data;
  339. struct aws_http_connection *connection;
  340. int error_code;
  341. struct aws_channel_task acquisition_task;
  342. };
  343. static void s_connection_acquisition_task(
  344. struct aws_channel_task *channel_task,
  345. void *arg,
  346. enum aws_task_status status) {
  347. (void)channel_task;
  348. struct aws_http_connection_acquisition *pending_acquisition = arg;
  349. /* this is a channel task. If it is canceled, that means the channel shutdown. In that case, that's equivalent
  350. * to a closed connection. */
  351. if (status != AWS_TASK_STATUS_RUN_READY) {
  352. AWS_LOGF_WARN(
  353. AWS_LS_HTTP_CONNECTION_MANAGER,
  354. "id=%p: Failed to complete connection acquisition because the connection was closed",
  355. (void *)pending_acquisition->manager);
  356. pending_acquisition->callback(NULL, AWS_ERROR_HTTP_CONNECTION_CLOSED, pending_acquisition->user_data);
  357. /* release it back to prevent a leak of the connection count. */
  358. aws_http_connection_manager_release_connection(pending_acquisition->manager, pending_acquisition->connection);
  359. } else {
  360. AWS_LOGF_DEBUG(
  361. AWS_LS_HTTP_CONNECTION_MANAGER,
  362. "id=%p: Successfully completed connection acquisition with connection id=%p",
  363. (void *)pending_acquisition->manager,
  364. (void *)pending_acquisition->connection);
  365. pending_acquisition->callback(
  366. pending_acquisition->connection, pending_acquisition->error_code, pending_acquisition->user_data);
  367. }
  368. aws_mem_release(pending_acquisition->allocator, pending_acquisition);
  369. }
  370. /*
  371. * Invokes a set of connection acquisition completion callbacks.
  372. *
  373. * Soft Requirement: The manager's lock must not be held in the callstack.
  374. *
  375. * Assumes that internal state (like pending_acquisition_count, vended_connection_count, etc...) have already been
  376. * updated according to the list's contents.
  377. */
  378. static void s_aws_http_connection_manager_complete_acquisitions(
  379. struct aws_linked_list *acquisitions,
  380. struct aws_allocator *allocator) {
  381. while (!aws_linked_list_empty(acquisitions)) {
  382. struct aws_linked_list_node *node = aws_linked_list_pop_front(acquisitions);
  383. struct aws_http_connection_acquisition *pending_acquisition =
  384. AWS_CONTAINER_OF(node, struct aws_http_connection_acquisition, node);
  385. if (pending_acquisition->error_code == AWS_OP_SUCCESS) {
  386. struct aws_channel *channel =
  387. pending_acquisition->manager->system_vtable->connection_get_channel(pending_acquisition->connection);
  388. AWS_PRECONDITION(channel);
  389. /* For some workloads, going ahead and moving the connection callback to the connection's thread is a
  390. * substantial performance improvement so let's do that */
  391. if (!pending_acquisition->manager->system_vtable->is_callers_thread(channel)) {
  392. aws_channel_task_init(
  393. &pending_acquisition->acquisition_task,
  394. s_connection_acquisition_task,
  395. pending_acquisition,
  396. "s_connection_acquisition_task");
  397. aws_channel_schedule_task_now(channel, &pending_acquisition->acquisition_task);
  398. return;
  399. }
  400. AWS_LOGF_DEBUG(
  401. AWS_LS_HTTP_CONNECTION_MANAGER,
  402. "id=%p: Successfully completed connection acquisition with connection id=%p",
  403. (void *)pending_acquisition->manager,
  404. (void *)pending_acquisition->connection);
  405. } else {
  406. AWS_LOGF_WARN(
  407. AWS_LS_HTTP_CONNECTION_MANAGER,
  408. "id=%p: Failed to complete connection acquisition with error_code %d(%s)",
  409. (void *)pending_acquisition->manager,
  410. pending_acquisition->error_code,
  411. aws_error_str(pending_acquisition->error_code));
  412. }
  413. pending_acquisition->callback(
  414. pending_acquisition->connection, pending_acquisition->error_code, pending_acquisition->user_data);
  415. aws_mem_release(allocator, pending_acquisition);
  416. }
  417. }
  418. /*
  419. * Moves the first pending connection acquisition into a (task set) list. Call this while holding the lock to
  420. * build the set of callbacks to be completed once the lock is released.
  421. *
  422. * Hard Requirement: Manager's lock must held somewhere in the call stack
  423. *
  424. * If this was a successful acquisition then connection is non-null
  425. * If this was a failed acquisition then connection is null and error_code is hopefully a useful diagnostic (extreme
  426. * edge cases exist where it may not be though)
  427. */
  428. static void s_aws_http_connection_manager_move_front_acquisition(
  429. struct aws_http_connection_manager *manager,
  430. struct aws_http_connection *connection,
  431. int error_code,
  432. struct aws_linked_list *output_list) {
  433. AWS_FATAL_ASSERT(!aws_linked_list_empty(&manager->pending_acquisitions));
  434. struct aws_linked_list_node *node = aws_linked_list_pop_front(&manager->pending_acquisitions);
  435. AWS_FATAL_ASSERT(manager->pending_acquisition_count > 0);
  436. --manager->pending_acquisition_count;
  437. if (error_code == AWS_ERROR_SUCCESS && connection == NULL) {
  438. AWS_LOGF_FATAL(
  439. AWS_LS_HTTP_CONNECTION_MANAGER,
  440. "id=%p: Connection acquisition completed with NULL connection and no error code. Investigate.",
  441. (void *)manager);
  442. error_code = AWS_ERROR_UNKNOWN;
  443. }
  444. struct aws_http_connection_acquisition *pending_acquisition =
  445. AWS_CONTAINER_OF(node, struct aws_http_connection_acquisition, node);
  446. pending_acquisition->connection = connection;
  447. pending_acquisition->error_code = error_code;
  448. aws_linked_list_push_back(output_list, node);
  449. }
  450. /*
  451. * Encompasses all of the external operations that need to be done for various
  452. * events:
  453. * manager release
  454. * connection release
  455. * connection acquire
  456. * connection_setup
  457. * connection_shutdown
  458. *
  459. * The transaction is built under the manager's lock (and the internal state is updated optimistically),
  460. * but then executed outside of it.
  461. */
  462. struct aws_connection_management_transaction {
  463. struct aws_http_connection_manager *manager;
  464. struct aws_allocator *allocator;
  465. struct aws_linked_list completions;
  466. struct aws_http_connection *connection_to_release;
  467. struct aws_linked_list connections_to_release; /* <struct aws_idle_connection> */
  468. struct aws_http_connection_manager_snapshot snapshot;
  469. size_t new_connections;
  470. };
  471. static void s_aws_connection_management_transaction_init(
  472. struct aws_connection_management_transaction *work,
  473. struct aws_http_connection_manager *manager) {
  474. AWS_ZERO_STRUCT(*work);
  475. aws_linked_list_init(&work->connections_to_release);
  476. aws_linked_list_init(&work->completions);
  477. work->manager = manager;
  478. work->allocator = manager->allocator;
  479. aws_ref_count_acquire(&manager->internal_ref_count);
  480. }
  481. static void s_aws_connection_management_transaction_clean_up(struct aws_connection_management_transaction *work) {
  482. AWS_FATAL_ASSERT(aws_linked_list_empty(&work->connections_to_release));
  483. AWS_FATAL_ASSERT(aws_linked_list_empty(&work->completions));
  484. AWS_ASSERT(work->manager);
  485. aws_ref_count_release(&work->manager->internal_ref_count);
  486. }
  487. /* The count acquire and release all needs to be invoked helding the lock */
  488. static void s_connection_manager_internal_ref_increase(
  489. struct aws_http_connection_manager *manager,
  490. enum aws_http_connection_manager_count_type count_type,
  491. size_t num) {
  492. manager->internal_ref[count_type] += num;
  493. for (size_t i = 0; i < num; i++) {
  494. aws_ref_count_acquire(&manager->internal_ref_count);
  495. }
  496. }
  497. static void s_connection_manager_internal_ref_decrease(
  498. struct aws_http_connection_manager *manager,
  499. enum aws_http_connection_manager_count_type count_type,
  500. size_t num) {
  501. manager->internal_ref[count_type] -= num;
  502. for (size_t i = 0; i < num; i++) {
  503. /* This only happens between transcation init and transcation clean up. As transcation always has a internal
  504. * refcount, this will never bring the refcount to zero */
  505. aws_ref_count_release(&manager->internal_ref_count);
  506. }
  507. }
  508. /* Only invoked with the lock held */
  509. static void s_aws_http_connection_manager_build_transaction(struct aws_connection_management_transaction *work) {
  510. struct aws_http_connection_manager *manager = work->manager;
  511. if (manager->state == AWS_HCMST_READY) {
  512. /*
  513. * Step 1 - If there's free connections, complete acquisition requests
  514. */
  515. while (!aws_linked_list_empty(&manager->idle_connections) > 0 && manager->pending_acquisition_count > 0) {
  516. AWS_FATAL_ASSERT(manager->idle_connection_count >= 1);
  517. /*
  518. * It is absolutely critical that this is pop_back and not front. By making the idle connections
  519. * a LIFO stack, the list will always be sorted from oldest (in terms of idle time) to newest. This means
  520. * we can always use the cull timestamp of the first connection as the next scheduled time for culling.
  521. * It also means that when we cull connections, we can quit the loop as soon as we find a connection
  522. * whose timestamp is greater than the current timestamp.
  523. */
  524. struct aws_linked_list_node *node = aws_linked_list_pop_back(&manager->idle_connections);
  525. struct aws_idle_connection *idle_connection = AWS_CONTAINER_OF(node, struct aws_idle_connection, node);
  526. struct aws_http_connection *connection = idle_connection->connection;
  527. AWS_LOGF_DEBUG(
  528. AWS_LS_HTTP_CONNECTION_MANAGER,
  529. "id=%p: Grabbing pooled connection (%p)",
  530. (void *)manager,
  531. (void *)connection);
  532. s_aws_http_connection_manager_move_front_acquisition(
  533. manager, connection, AWS_ERROR_SUCCESS, &work->completions);
  534. s_connection_manager_internal_ref_increase(manager, AWS_HCMCT_VENDED_CONNECTION, 1);
  535. --manager->idle_connection_count;
  536. aws_mem_release(idle_connection->allocator, idle_connection);
  537. }
  538. /*
  539. * Step 2 - if there's excess pending acquisitions and we have room to make more, make more
  540. */
  541. if (manager->pending_acquisition_count >
  542. manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] + manager->pending_settings_count) {
  543. AWS_FATAL_ASSERT(
  544. manager->max_connections >= manager->internal_ref[AWS_HCMCT_VENDED_CONNECTION] +
  545. manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] +
  546. manager->pending_settings_count);
  547. work->new_connections = manager->pending_acquisition_count -
  548. manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] -
  549. manager->pending_settings_count;
  550. size_t max_new_connections =
  551. manager->max_connections -
  552. (manager->internal_ref[AWS_HCMCT_VENDED_CONNECTION] +
  553. manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] + manager->pending_settings_count);
  554. if (work->new_connections > max_new_connections) {
  555. work->new_connections = max_new_connections;
  556. }
  557. s_connection_manager_internal_ref_increase(manager, AWS_HCMCT_PENDING_CONNECTIONS, work->new_connections);
  558. }
  559. } else {
  560. /*
  561. * swap our internal connection set with the empty work set
  562. */
  563. AWS_FATAL_ASSERT(aws_linked_list_empty(&work->connections_to_release));
  564. aws_linked_list_swap_contents(&manager->idle_connections, &work->connections_to_release);
  565. manager->idle_connection_count = 0;
  566. /*
  567. * Move all manager pending acquisitions to the work completion list
  568. */
  569. while (!aws_linked_list_empty(&manager->pending_acquisitions)) {
  570. AWS_LOGF_DEBUG(
  571. AWS_LS_HTTP_CONNECTION_MANAGER,
  572. "id=%p: Failing pending connection acquisition due to manager shut down",
  573. (void *)manager);
  574. s_aws_http_connection_manager_move_front_acquisition(
  575. manager, NULL, AWS_ERROR_HTTP_CONNECTION_MANAGER_SHUTTING_DOWN, &work->completions);
  576. }
  577. AWS_LOGF_INFO(
  578. AWS_LS_HTTP_CONNECTION_MANAGER,
  579. "id=%p: manager release, failing %zu pending acquisitions",
  580. (void *)manager,
  581. manager->pending_acquisition_count);
  582. manager->pending_acquisition_count = 0;
  583. }
  584. s_aws_http_connection_manager_get_snapshot(manager, &work->snapshot);
  585. }
  586. static void s_aws_http_connection_manager_execute_transaction(struct aws_connection_management_transaction *work);
  587. /*
  588. * The final last gasp of a connection manager where memory is cleaned up. Destruction is split up into two parts,
  589. * a begin and a finish. Idle connection culling requires a scheduled task on an arbitrary event loop. If idle
  590. * connection culling is on then this task must be cancelled before destruction can finish, but you can only cancel
  591. * a task from the same event loop that it is scheduled on. To resolve this, when using idle connection culling,
  592. * we schedule a finish destruction task on the event loop that the culling task is on. This finish task
  593. * cancels the culling task and then calls this function. If we are not using idle connection culling, we can
  594. * call this function immediately from the start of destruction.
  595. */
  596. static void s_aws_http_connection_manager_finish_destroy(struct aws_http_connection_manager *manager) {
  597. if (manager == NULL) {
  598. return;
  599. }
  600. AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: Destroying self", (void *)manager);
  601. AWS_FATAL_ASSERT(manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] == 0);
  602. AWS_FATAL_ASSERT(manager->pending_settings_count == 0);
  603. AWS_FATAL_ASSERT(manager->internal_ref[AWS_HCMCT_VENDED_CONNECTION] == 0);
  604. AWS_FATAL_ASSERT(manager->pending_acquisition_count == 0);
  605. AWS_FATAL_ASSERT(manager->internal_ref[AWS_HCMCT_OPEN_CONNECTION] == 0);
  606. AWS_FATAL_ASSERT(aws_linked_list_empty(&manager->pending_acquisitions));
  607. AWS_FATAL_ASSERT(aws_linked_list_empty(&manager->idle_connections));
  608. aws_string_destroy(manager->host);
  609. if (manager->initial_settings) {
  610. aws_array_list_clean_up(manager->initial_settings);
  611. aws_mem_release(manager->allocator, manager->initial_settings);
  612. }
  613. if (manager->tls_connection_options) {
  614. aws_tls_connection_options_clean_up(manager->tls_connection_options);
  615. aws_mem_release(manager->allocator, manager->tls_connection_options);
  616. }
  617. if (manager->proxy_ev_tls_options) {
  618. aws_tls_connection_options_clean_up(manager->proxy_ev_tls_options);
  619. aws_mem_release(manager->allocator, manager->proxy_ev_tls_options);
  620. }
  621. if (manager->proxy_config) {
  622. aws_http_proxy_config_destroy(manager->proxy_config);
  623. }
  624. /*
  625. * If this task exists then we are actually in the corresponding event loop running the final destruction task.
  626. * In that case, we've already cancelled this task and when you cancel, it runs synchronously. So in that
  627. * case the task has run as cancelled, it was not rescheduled, and so we can safely release the memory.
  628. */
  629. if (manager->cull_task) {
  630. aws_mem_release(manager->allocator, manager->cull_task);
  631. }
  632. aws_mutex_clean_up(&manager->lock);
  633. aws_client_bootstrap_release(manager->bootstrap);
  634. if (manager->shutdown_complete_callback) {
  635. manager->shutdown_complete_callback(manager->shutdown_complete_user_data);
  636. }
  637. aws_mem_release(manager->allocator, manager);
  638. }
  639. /* This is scheduled to run on the cull task's event loop. Should only be scheduled to run if we have one */
  640. static void s_final_destruction_task(struct aws_task *task, void *arg, enum aws_task_status status) {
  641. (void)status;
  642. struct aws_http_connection_manager *manager = arg;
  643. struct aws_allocator *allocator = manager->allocator;
  644. AWS_FATAL_ASSERT(manager->cull_task != NULL);
  645. AWS_FATAL_ASSERT(manager->cull_event_loop != NULL);
  646. aws_event_loop_cancel_task(manager->cull_event_loop, manager->cull_task);
  647. aws_mem_release(allocator, task);
  648. /* release the refcount on manager as the culling task will not run again */
  649. aws_ref_count_release(&manager->internal_ref_count);
  650. }
  651. static void s_cull_task(struct aws_task *task, void *arg, enum aws_task_status status);
  652. static void s_schedule_connection_culling(struct aws_http_connection_manager *manager) {
  653. if (manager->max_connection_idle_in_milliseconds == 0) {
  654. return;
  655. }
  656. if (manager->cull_task == NULL) {
  657. manager->cull_task = aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_task));
  658. aws_task_init(manager->cull_task, s_cull_task, manager, "cull_idle_connections");
  659. /* For the task to properly run and cancel, we need to keep manager alive */
  660. aws_ref_count_acquire(&manager->internal_ref_count);
  661. }
  662. if (manager->cull_event_loop == NULL) {
  663. manager->cull_event_loop = aws_event_loop_group_get_next_loop(manager->bootstrap->event_loop_group);
  664. }
  665. AWS_FATAL_ASSERT(manager->cull_event_loop != NULL);
  666. uint64_t cull_task_time = 0;
  667. aws_mutex_lock(&manager->lock);
  668. const struct aws_linked_list_node *end = aws_linked_list_end(&manager->idle_connections);
  669. struct aws_linked_list_node *oldest_node = aws_linked_list_begin(&manager->idle_connections);
  670. if (oldest_node != end) {
  671. /*
  672. * Since the connections are in LIFO order in the list, the front of the list has the closest
  673. * cull time.
  674. */
  675. struct aws_idle_connection *oldest_idle_connection =
  676. AWS_CONTAINER_OF(oldest_node, struct aws_idle_connection, node);
  677. cull_task_time = oldest_idle_connection->cull_timestamp;
  678. } else {
  679. /*
  680. * There are no connections in the list, so the absolute minimum anything could be culled is the full
  681. * culling interval from now.
  682. */
  683. uint64_t now = 0;
  684. manager->system_vtable->get_monotonic_time(&now);
  685. cull_task_time =
  686. now + aws_timestamp_convert(
  687. manager->max_connection_idle_in_milliseconds, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL);
  688. }
  689. aws_mutex_unlock(&manager->lock);
  690. aws_event_loop_schedule_task_future(manager->cull_event_loop, manager->cull_task, cull_task_time);
  691. return;
  692. }
  693. struct aws_http_connection_manager *aws_http_connection_manager_new(
  694. struct aws_allocator *allocator,
  695. const struct aws_http_connection_manager_options *options) {
  696. aws_http_fatal_assert_library_initialized();
  697. if (!options) {
  698. AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION_MANAGER, "Invalid options - options is null");
  699. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  700. return NULL;
  701. }
  702. if (!options->socket_options) {
  703. AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION_MANAGER, "Invalid options - socket_options is null");
  704. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  705. return NULL;
  706. }
  707. if (options->max_connections == 0) {
  708. AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION_MANAGER, "Invalid options - max_connections cannot be 0");
  709. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  710. return NULL;
  711. }
  712. if (options->tls_connection_options && options->http2_prior_knowledge) {
  713. AWS_LOGF_ERROR(
  714. AWS_LS_HTTP_CONNECTION_MANAGER, "Invalid options - HTTP/2 prior knowledge cannot be set when TLS is used");
  715. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  716. return NULL;
  717. }
  718. struct aws_http_connection_manager *manager =
  719. aws_mem_calloc(allocator, 1, sizeof(struct aws_http_connection_manager));
  720. if (manager == NULL) {
  721. return NULL;
  722. }
  723. manager->allocator = allocator;
  724. if (aws_mutex_init(&manager->lock)) {
  725. goto on_error;
  726. }
  727. aws_ref_count_init(
  728. &manager->internal_ref_count,
  729. manager,
  730. (aws_simple_completion_callback *)s_aws_http_connection_manager_finish_destroy);
  731. aws_linked_list_init(&manager->idle_connections);
  732. aws_linked_list_init(&manager->pending_acquisitions);
  733. manager->host = aws_string_new_from_cursor(allocator, &options->host);
  734. if (manager->host == NULL) {
  735. goto on_error;
  736. }
  737. if (options->tls_connection_options) {
  738. manager->tls_connection_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options));
  739. if (aws_tls_connection_options_copy(manager->tls_connection_options, options->tls_connection_options)) {
  740. goto on_error;
  741. }
  742. }
  743. if (options->proxy_options) {
  744. manager->proxy_config = aws_http_proxy_config_new_from_manager_options(allocator, options);
  745. if (manager->proxy_config == NULL) {
  746. goto on_error;
  747. }
  748. }
  749. if (options->monitoring_options) {
  750. manager->monitoring_options = *options->monitoring_options;
  751. }
  752. manager->state = AWS_HCMST_READY;
  753. manager->initial_window_size = options->initial_window_size;
  754. manager->port = options->port;
  755. manager->max_connections = options->max_connections;
  756. manager->socket_options = *options->socket_options;
  757. manager->bootstrap = aws_client_bootstrap_acquire(options->bootstrap);
  758. manager->system_vtable = g_aws_http_connection_manager_default_system_vtable_ptr;
  759. manager->external_ref_count = 1;
  760. manager->shutdown_complete_callback = options->shutdown_complete_callback;
  761. manager->shutdown_complete_user_data = options->shutdown_complete_user_data;
  762. manager->enable_read_back_pressure = options->enable_read_back_pressure;
  763. manager->max_connection_idle_in_milliseconds = options->max_connection_idle_in_milliseconds;
  764. if (options->proxy_ev_settings) {
  765. manager->proxy_ev_settings = *options->proxy_ev_settings;
  766. }
  767. if (manager->proxy_ev_settings.tls_options) {
  768. manager->proxy_ev_tls_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_tls_connection_options));
  769. if (aws_tls_connection_options_copy(manager->proxy_ev_tls_options, manager->proxy_ev_settings.tls_options)) {
  770. goto on_error;
  771. }
  772. manager->proxy_ev_settings.tls_options = manager->proxy_ev_tls_options;
  773. }
  774. manager->http2_prior_knowledge = options->http2_prior_knowledge;
  775. if (options->num_initial_settings > 0) {
  776. manager->initial_settings = aws_mem_calloc(allocator, 1, sizeof(struct aws_array_list));
  777. aws_array_list_init_dynamic(
  778. manager->initial_settings, allocator, options->num_initial_settings, sizeof(struct aws_http2_setting));
  779. memcpy(
  780. manager->initial_settings->data,
  781. options->initial_settings_array,
  782. options->num_initial_settings * sizeof(struct aws_http2_setting));
  783. }
  784. manager->max_closed_streams = options->max_closed_streams;
  785. manager->http2_conn_manual_window_management = options->http2_conn_manual_window_management;
  786. /* NOTHING can fail after here */
  787. s_schedule_connection_culling(manager);
  788. AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: Successfully created", (void *)manager);
  789. return manager;
  790. on_error:
  791. s_aws_http_connection_manager_finish_destroy(manager);
  792. return NULL;
  793. }
  794. void aws_http_connection_manager_acquire(struct aws_http_connection_manager *manager) {
  795. aws_mutex_lock(&manager->lock);
  796. AWS_FATAL_ASSERT(manager->external_ref_count > 0);
  797. manager->external_ref_count += 1;
  798. aws_mutex_unlock(&manager->lock);
  799. }
  800. void aws_http_connection_manager_release(struct aws_http_connection_manager *manager) {
  801. struct aws_connection_management_transaction work;
  802. s_aws_connection_management_transaction_init(&work, manager);
  803. AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: release", (void *)manager);
  804. aws_mutex_lock(&manager->lock);
  805. if (manager->external_ref_count > 0) {
  806. manager->external_ref_count -= 1;
  807. if (manager->external_ref_count == 0) {
  808. AWS_LOGF_INFO(
  809. AWS_LS_HTTP_CONNECTION_MANAGER,
  810. "id=%p: ref count now zero, starting shut down process",
  811. (void *)manager);
  812. manager->state = AWS_HCMST_SHUTTING_DOWN;
  813. s_aws_http_connection_manager_build_transaction(&work);
  814. if (manager->cull_task != NULL) {
  815. /* When manager shutting down, schedule the task to cancel the cull task if exist. */
  816. AWS_FATAL_ASSERT(manager->cull_event_loop);
  817. struct aws_task *final_destruction_task =
  818. aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_task));
  819. aws_task_init(final_destruction_task, s_final_destruction_task, manager, "final_scheduled_destruction");
  820. aws_event_loop_schedule_task_now(manager->cull_event_loop, final_destruction_task);
  821. }
  822. aws_ref_count_release(&manager->internal_ref_count);
  823. }
  824. } else {
  825. AWS_LOGF_ERROR(
  826. AWS_LS_HTTP_CONNECTION_MANAGER,
  827. "id=%p: Connection manager release called with a zero reference count",
  828. (void *)manager);
  829. }
  830. aws_mutex_unlock(&manager->lock);
  831. s_aws_http_connection_manager_execute_transaction(&work);
  832. }
  833. static void s_aws_http_connection_manager_on_connection_setup(
  834. struct aws_http_connection *connection,
  835. int error_code,
  836. void *user_data);
  837. static void s_aws_http_connection_manager_on_connection_shutdown(
  838. struct aws_http_connection *connection,
  839. int error_code,
  840. void *user_data);
  841. static void s_aws_http_connection_manager_h2_on_goaway_received(
  842. struct aws_http_connection *http2_connection,
  843. uint32_t last_stream_id,
  844. uint32_t http2_error_code,
  845. struct aws_byte_cursor debug_data,
  846. void *user_data);
  847. static void s_aws_http_connection_manager_h2_on_initial_settings_completed(
  848. struct aws_http_connection *http2_connection,
  849. int error_code,
  850. void *user_data);
  851. static int s_aws_http_connection_manager_new_connection(struct aws_http_connection_manager *manager) {
  852. struct aws_http_client_connection_options options;
  853. AWS_ZERO_STRUCT(options);
  854. options.self_size = sizeof(struct aws_http_client_connection_options);
  855. options.bootstrap = manager->bootstrap;
  856. options.tls_options = manager->tls_connection_options;
  857. options.allocator = manager->allocator;
  858. options.user_data = manager;
  859. options.host_name = aws_byte_cursor_from_string(manager->host);
  860. options.port = manager->port;
  861. options.initial_window_size = manager->initial_window_size;
  862. options.socket_options = &manager->socket_options;
  863. options.on_setup = s_aws_http_connection_manager_on_connection_setup;
  864. options.on_shutdown = s_aws_http_connection_manager_on_connection_shutdown;
  865. options.manual_window_management = manager->enable_read_back_pressure;
  866. options.proxy_ev_settings = &manager->proxy_ev_settings;
  867. options.prior_knowledge_http2 = manager->http2_prior_knowledge;
  868. struct aws_http2_connection_options h2_options;
  869. AWS_ZERO_STRUCT(h2_options);
  870. if (manager->initial_settings) {
  871. h2_options.initial_settings_array = manager->initial_settings->data;
  872. h2_options.num_initial_settings = aws_array_list_length(manager->initial_settings);
  873. }
  874. h2_options.max_closed_streams = manager->max_closed_streams;
  875. h2_options.conn_manual_window_management = manager->http2_conn_manual_window_management;
  876. /* The initial_settings_completed invoked after the other side acknowledges it, and will always be invoked if the
  877. * connection set up */
  878. h2_options.on_initial_settings_completed = s_aws_http_connection_manager_h2_on_initial_settings_completed;
  879. h2_options.on_goaway_received = s_aws_http_connection_manager_h2_on_goaway_received;
  880. options.http2_options = &h2_options;
  881. if (aws_http_connection_monitoring_options_is_valid(&manager->monitoring_options)) {
  882. options.monitoring_options = &manager->monitoring_options;
  883. }
  884. struct aws_http_proxy_options proxy_options;
  885. AWS_ZERO_STRUCT(proxy_options);
  886. if (manager->proxy_config) {
  887. aws_http_proxy_options_init_from_config(&proxy_options, manager->proxy_config);
  888. options.proxy_options = &proxy_options;
  889. }
  890. if (manager->system_vtable->create_connection(&options)) {
  891. AWS_LOGF_ERROR(
  892. AWS_LS_HTTP_CONNECTION_MANAGER,
  893. "id=%p: http connection creation failed with error code %d(%s)",
  894. (void *)manager,
  895. aws_last_error(),
  896. aws_error_str(aws_last_error()));
  897. return AWS_OP_ERR;
  898. }
  899. return AWS_OP_SUCCESS;
  900. }
  901. static void s_aws_http_connection_manager_execute_transaction(struct aws_connection_management_transaction *work) {
  902. struct aws_http_connection_manager *manager = work->manager;
  903. int representative_error = 0;
  904. size_t new_connection_failures = 0;
  905. /*
  906. * Step 1 - Logging
  907. */
  908. s_aws_http_connection_manager_log_snapshot(manager, &work->snapshot);
  909. /*
  910. * Step 2 - Perform any requested connection releases
  911. */
  912. while (!aws_linked_list_empty(&work->connections_to_release)) {
  913. struct aws_linked_list_node *node = aws_linked_list_pop_back(&work->connections_to_release);
  914. struct aws_idle_connection *idle_connection = AWS_CONTAINER_OF(node, struct aws_idle_connection, node);
  915. AWS_LOGF_INFO(
  916. AWS_LS_HTTP_CONNECTION_MANAGER,
  917. "id=%p: Releasing connection (id=%p)",
  918. (void *)manager,
  919. (void *)idle_connection->connection);
  920. manager->system_vtable->release_connection(idle_connection->connection);
  921. aws_mem_release(idle_connection->allocator, idle_connection);
  922. }
  923. if (work->connection_to_release) {
  924. AWS_LOGF_INFO(
  925. AWS_LS_HTTP_CONNECTION_MANAGER,
  926. "id=%p: Releasing connection (id=%p)",
  927. (void *)manager,
  928. (void *)work->connection_to_release);
  929. manager->system_vtable->release_connection(work->connection_to_release);
  930. }
  931. /*
  932. * Step 3 - Make new connections
  933. */
  934. struct aws_array_list errors;
  935. AWS_ZERO_STRUCT(errors);
  936. /* Even if we can't init this array, we still need to invoke error callbacks properly */
  937. bool push_errors = false;
  938. if (work->new_connections > 0) {
  939. AWS_LOGF_INFO(
  940. AWS_LS_HTTP_CONNECTION_MANAGER,
  941. "id=%p: Requesting %zu new connections from http",
  942. (void *)manager,
  943. work->new_connections);
  944. push_errors = aws_array_list_init_dynamic(&errors, work->allocator, work->new_connections, sizeof(int)) ==
  945. AWS_ERROR_SUCCESS;
  946. }
  947. for (size_t i = 0; i < work->new_connections; ++i) {
  948. if (s_aws_http_connection_manager_new_connection(manager)) {
  949. ++new_connection_failures;
  950. representative_error = aws_last_error();
  951. if (push_errors) {
  952. AWS_FATAL_ASSERT(aws_array_list_push_back(&errors, &representative_error) == AWS_OP_SUCCESS);
  953. }
  954. }
  955. }
  956. if (new_connection_failures > 0) {
  957. /*
  958. * We failed and aren't going to receive a callback, but the current state assumes we will receive
  959. * a callback. So we need to re-lock and update the state ourselves.
  960. */
  961. aws_mutex_lock(&manager->lock);
  962. AWS_FATAL_ASSERT(manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] >= new_connection_failures);
  963. s_connection_manager_internal_ref_decrease(manager, AWS_HCMCT_PENDING_CONNECTIONS, new_connection_failures);
  964. /*
  965. * Rather than failing one acquisition for each connection failure, if there's at least one
  966. * connection failure, we instead fail all excess acquisitions, since there's no pending
  967. * connect that will necessarily resolve them.
  968. *
  969. * Try to correspond an error with the acquisition failure, but as a fallback just use the
  970. * representative error.
  971. */
  972. size_t i = 0;
  973. while (manager->pending_acquisition_count > manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS]) {
  974. int error = representative_error;
  975. if (i < aws_array_list_length(&errors)) {
  976. aws_array_list_get_at(&errors, &error, i);
  977. }
  978. AWS_LOGF_DEBUG(
  979. AWS_LS_HTTP_CONNECTION_MANAGER,
  980. "id=%p: Failing excess connection acquisition with error code %d",
  981. (void *)manager,
  982. (int)error);
  983. s_aws_http_connection_manager_move_front_acquisition(manager, NULL, error, &work->completions);
  984. ++i;
  985. }
  986. aws_mutex_unlock(&manager->lock);
  987. }
  988. /*
  989. * Step 4 - Perform acquisition callbacks
  990. */
  991. s_aws_http_connection_manager_complete_acquisitions(&work->completions, work->allocator);
  992. aws_array_list_clean_up(&errors);
  993. /*
  994. * Step 5 - Clean up work. Do this here rather than at the end of every caller. Destroy the manager if necessary
  995. */
  996. s_aws_connection_management_transaction_clean_up(work);
  997. }
  998. void aws_http_connection_manager_acquire_connection(
  999. struct aws_http_connection_manager *manager,
  1000. aws_http_connection_manager_on_connection_setup_fn *callback,
  1001. void *user_data) {
  1002. AWS_LOGF_DEBUG(AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: Acquire connection", (void *)manager);
  1003. struct aws_http_connection_acquisition *request =
  1004. aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_http_connection_acquisition));
  1005. request->allocator = manager->allocator;
  1006. request->callback = callback;
  1007. request->user_data = user_data;
  1008. request->manager = manager;
  1009. struct aws_connection_management_transaction work;
  1010. s_aws_connection_management_transaction_init(&work, manager);
  1011. aws_mutex_lock(&manager->lock);
  1012. /* It's a use after free crime, we don't want to handle */
  1013. AWS_FATAL_ASSERT(manager->state == AWS_HCMST_READY);
  1014. aws_linked_list_push_back(&manager->pending_acquisitions, &request->node);
  1015. ++manager->pending_acquisition_count;
  1016. s_aws_http_connection_manager_build_transaction(&work);
  1017. aws_mutex_unlock(&manager->lock);
  1018. s_aws_http_connection_manager_execute_transaction(&work);
  1019. }
  1020. /* Only invoke with lock held */
  1021. static int s_idle_connection(struct aws_http_connection_manager *manager, struct aws_http_connection *connection) {
  1022. struct aws_idle_connection *idle_connection =
  1023. aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_idle_connection));
  1024. idle_connection->allocator = manager->allocator;
  1025. idle_connection->connection = connection;
  1026. uint64_t idle_start_timestamp = 0;
  1027. if (manager->system_vtable->get_monotonic_time(&idle_start_timestamp)) {
  1028. goto on_error;
  1029. }
  1030. idle_connection->cull_timestamp =
  1031. idle_start_timestamp +
  1032. aws_timestamp_convert(
  1033. manager->max_connection_idle_in_milliseconds, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL);
  1034. aws_linked_list_push_back(&manager->idle_connections, &idle_connection->node);
  1035. ++manager->idle_connection_count;
  1036. return AWS_OP_SUCCESS;
  1037. on_error:
  1038. aws_mem_release(idle_connection->allocator, idle_connection);
  1039. return AWS_OP_ERR;
  1040. }
  1041. int aws_http_connection_manager_release_connection(
  1042. struct aws_http_connection_manager *manager,
  1043. struct aws_http_connection *connection) {
  1044. struct aws_connection_management_transaction work;
  1045. s_aws_connection_management_transaction_init(&work, manager);
  1046. int result = AWS_OP_ERR;
  1047. bool should_release_connection = !manager->system_vtable->is_connection_available(connection);
  1048. AWS_LOGF_DEBUG(
  1049. AWS_LS_HTTP_CONNECTION_MANAGER,
  1050. "id=%p: User releasing connection (id=%p)",
  1051. (void *)manager,
  1052. (void *)connection);
  1053. aws_mutex_lock(&manager->lock);
  1054. /* We're probably hosed in this case, but let's not underflow */
  1055. if (manager->internal_ref[AWS_HCMCT_VENDED_CONNECTION] == 0) {
  1056. AWS_LOGF_FATAL(
  1057. AWS_LS_HTTP_CONNECTION_MANAGER,
  1058. "id=%p: Connection released when vended connection count is zero",
  1059. (void *)manager);
  1060. aws_raise_error(AWS_ERROR_HTTP_CONNECTION_MANAGER_VENDED_CONNECTION_UNDERFLOW);
  1061. goto release;
  1062. }
  1063. result = AWS_OP_SUCCESS;
  1064. s_connection_manager_internal_ref_decrease(manager, AWS_HCMCT_VENDED_CONNECTION, 1);
  1065. if (!should_release_connection) {
  1066. if (s_idle_connection(manager, connection)) {
  1067. should_release_connection = true;
  1068. }
  1069. }
  1070. s_aws_http_connection_manager_build_transaction(&work);
  1071. if (should_release_connection) {
  1072. work.connection_to_release = connection;
  1073. }
  1074. release:
  1075. aws_mutex_unlock(&manager->lock);
  1076. s_aws_http_connection_manager_execute_transaction(&work);
  1077. return result;
  1078. }
  1079. static void s_aws_http_connection_manager_h2_on_goaway_received(
  1080. struct aws_http_connection *http2_connection,
  1081. uint32_t last_stream_id,
  1082. uint32_t http2_error_code,
  1083. struct aws_byte_cursor debug_data,
  1084. void *user_data) {
  1085. struct aws_http_connection_manager *manager = user_data;
  1086. /* We don't offer user the details, but we can still log it out for debugging */
  1087. AWS_LOGF_DEBUG(
  1088. AWS_LS_HTTP_CONNECTION_MANAGER,
  1089. "id=%p: HTTP/2 connection (id=%p) received GOAWAY with: last stream id - %u, error code - %u, debug data - "
  1090. "\"%.*s\"",
  1091. (void *)manager,
  1092. (void *)http2_connection,
  1093. last_stream_id,
  1094. http2_error_code,
  1095. (int)debug_data.len,
  1096. debug_data.ptr);
  1097. struct aws_connection_management_transaction work;
  1098. s_aws_connection_management_transaction_init(&work, manager);
  1099. aws_mutex_lock(&manager->lock);
  1100. /* Goaway received, remove the connection from idle and release it, if it's there. But, not decrease the
  1101. * open_connection_count as the shutdown callback will be invoked, we still need the manager to be alive */
  1102. const struct aws_linked_list_node *end = aws_linked_list_end(&manager->idle_connections);
  1103. for (struct aws_linked_list_node *node = aws_linked_list_begin(&manager->idle_connections); node != end;
  1104. node = aws_linked_list_next(node)) {
  1105. struct aws_idle_connection *current_idle_connection = AWS_CONTAINER_OF(node, struct aws_idle_connection, node);
  1106. if (current_idle_connection->connection == http2_connection) {
  1107. aws_linked_list_remove(node);
  1108. work.connection_to_release = http2_connection;
  1109. aws_mem_release(current_idle_connection->allocator, current_idle_connection);
  1110. --manager->idle_connection_count;
  1111. break;
  1112. }
  1113. }
  1114. s_aws_http_connection_manager_build_transaction(&work);
  1115. aws_mutex_unlock(&manager->lock);
  1116. s_aws_http_connection_manager_execute_transaction(&work);
  1117. }
  1118. /* Only invoke with lock held */
  1119. static void s_cm_on_connection_ready_or_failed(
  1120. struct aws_http_connection_manager *manager,
  1121. int error_code,
  1122. struct aws_http_connection *connection,
  1123. struct aws_connection_management_transaction *work) {
  1124. bool is_shutting_down = manager->state == AWS_HCMST_SHUTTING_DOWN;
  1125. if (!error_code) {
  1126. if (is_shutting_down || s_idle_connection(manager, connection)) {
  1127. /*
  1128. * release it immediately
  1129. */
  1130. AWS_LOGF_DEBUG(
  1131. AWS_LS_HTTP_CONNECTION_MANAGER,
  1132. "id=%p: New connection (id=%p) releasing immediately",
  1133. (void *)manager,
  1134. (void *)connection);
  1135. work->connection_to_release = connection;
  1136. }
  1137. } else {
  1138. /* fail acquisition as one connection cannot be used any more */
  1139. while (manager->pending_acquisition_count >
  1140. manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] + manager->pending_settings_count) {
  1141. AWS_LOGF_DEBUG(
  1142. AWS_LS_HTTP_CONNECTION_MANAGER,
  1143. "id=%p: Failing excess connection acquisition with error code %d",
  1144. (void *)manager,
  1145. (int)error_code);
  1146. s_aws_http_connection_manager_move_front_acquisition(manager, NULL, error_code, &work->completions);
  1147. }
  1148. /* Since the connection never being idle, we need to release the connection here. */
  1149. if (connection) {
  1150. work->connection_to_release = connection;
  1151. }
  1152. }
  1153. }
  1154. static void s_aws_http_connection_manager_h2_on_initial_settings_completed(
  1155. struct aws_http_connection *http2_connection,
  1156. int error_code,
  1157. void *user_data) {
  1158. struct aws_http_connection_manager *manager = user_data;
  1159. /* The other side acknowledge about the settings which also means we received the settings from other side at this
  1160. * point, because the settings should be the fist frame to be sent */
  1161. struct aws_connection_management_transaction work;
  1162. s_aws_connection_management_transaction_init(&work, manager);
  1163. AWS_LOGF_DEBUG(
  1164. AWS_LS_HTTP_CONNECTION_MANAGER,
  1165. "id=%p: HTTP/2 connection (id=%p) completed initial settings",
  1166. (void *)manager,
  1167. (void *)http2_connection);
  1168. aws_mutex_lock(&manager->lock);
  1169. AWS_FATAL_ASSERT(manager->pending_settings_count > 0);
  1170. --manager->pending_settings_count;
  1171. s_cm_on_connection_ready_or_failed(manager, error_code, http2_connection, &work);
  1172. s_aws_http_connection_manager_build_transaction(&work);
  1173. aws_mutex_unlock(&manager->lock);
  1174. s_aws_http_connection_manager_execute_transaction(&work);
  1175. }
  1176. static void s_aws_http_connection_manager_on_connection_setup(
  1177. struct aws_http_connection *connection,
  1178. int error_code,
  1179. void *user_data) {
  1180. struct aws_http_connection_manager *manager = user_data;
  1181. struct aws_connection_management_transaction work;
  1182. s_aws_connection_management_transaction_init(&work, manager);
  1183. if (connection != NULL) {
  1184. AWS_LOGF_DEBUG(
  1185. AWS_LS_HTTP_CONNECTION_MANAGER,
  1186. "id=%p: Received new connection (id=%p) from http layer",
  1187. (void *)manager,
  1188. (void *)connection);
  1189. } else {
  1190. AWS_LOGF_WARN(
  1191. AWS_LS_HTTP_CONNECTION_MANAGER,
  1192. "id=%p: Failed to obtain new connection from http layer, error %d(%s)",
  1193. (void *)manager,
  1194. error_code,
  1195. aws_error_str(error_code));
  1196. }
  1197. aws_mutex_lock(&manager->lock);
  1198. AWS_FATAL_ASSERT(manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] > 0);
  1199. s_connection_manager_internal_ref_decrease(manager, AWS_HCMCT_PENDING_CONNECTIONS, 1);
  1200. if (!error_code) {
  1201. /* Shutdown will not be invoked if setup completed with error */
  1202. s_connection_manager_internal_ref_increase(manager, AWS_HCMCT_OPEN_CONNECTION, 1);
  1203. }
  1204. if (connection != NULL && manager->system_vtable->connection_get_version(connection) == AWS_HTTP_VERSION_2) {
  1205. /* If the manager is shutting down, we will still wait for the settings, since we don't have map for connections
  1206. */
  1207. ++manager->pending_settings_count;
  1208. /* For http/2 connection, we vent the connection after the initial settings completed for the user to make
  1209. * sure the connection is really ready to use. So, we can revert the counting and act like nothing happens
  1210. * here and wait for the on_initial_settings_completed, which will ALWAYS be invoked before shutdown. BUT,
  1211. * we increase the open_connection_count, as the shutdown will be invoked no matter what happens. */
  1212. AWS_LOGF_TRACE(
  1213. AWS_LS_HTTP_CONNECTION_MANAGER,
  1214. "id=%p: New HTTP/2 connection (id=%p) set up, waiting for initial settings to complete",
  1215. (void *)manager,
  1216. (void *)connection);
  1217. } else {
  1218. /* If there is no connection, error code cannot be zero */
  1219. AWS_ASSERT(connection || error_code);
  1220. s_cm_on_connection_ready_or_failed(manager, error_code, connection, &work);
  1221. }
  1222. s_aws_http_connection_manager_build_transaction(&work);
  1223. aws_mutex_unlock(&manager->lock);
  1224. s_aws_http_connection_manager_execute_transaction(&work);
  1225. }
  1226. static void s_aws_http_connection_manager_on_connection_shutdown(
  1227. struct aws_http_connection *connection,
  1228. int error_code,
  1229. void *user_data) {
  1230. (void)error_code;
  1231. struct aws_http_connection_manager *manager = user_data;
  1232. AWS_LOGF_DEBUG(
  1233. AWS_LS_HTTP_CONNECTION_MANAGER,
  1234. "id=%p: shutdown received for connection (id=%p)",
  1235. (void *)manager,
  1236. (void *)connection);
  1237. struct aws_connection_management_transaction work;
  1238. s_aws_connection_management_transaction_init(&work, manager);
  1239. aws_mutex_lock(&manager->lock);
  1240. AWS_FATAL_ASSERT(manager->internal_ref[AWS_HCMCT_OPEN_CONNECTION] > 0);
  1241. s_connection_manager_internal_ref_decrease(manager, AWS_HCMCT_OPEN_CONNECTION, 1);
  1242. /*
  1243. * Find and, if found, remove it from idle connections
  1244. */
  1245. const struct aws_linked_list_node *end = aws_linked_list_end(&manager->idle_connections);
  1246. for (struct aws_linked_list_node *node = aws_linked_list_begin(&manager->idle_connections); node != end;
  1247. node = aws_linked_list_next(node)) {
  1248. struct aws_idle_connection *current_idle_connection = AWS_CONTAINER_OF(node, struct aws_idle_connection, node);
  1249. if (current_idle_connection->connection == connection) {
  1250. aws_linked_list_remove(node);
  1251. work.connection_to_release = connection;
  1252. aws_mem_release(current_idle_connection->allocator, current_idle_connection);
  1253. --manager->idle_connection_count;
  1254. break;
  1255. }
  1256. }
  1257. s_aws_http_connection_manager_build_transaction(&work);
  1258. aws_mutex_unlock(&manager->lock);
  1259. s_aws_http_connection_manager_execute_transaction(&work);
  1260. }
  1261. static void s_cull_idle_connections(struct aws_http_connection_manager *manager) {
  1262. AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: culling idle connections", (void *)manager);
  1263. if (manager == NULL || manager->max_connection_idle_in_milliseconds == 0) {
  1264. return;
  1265. }
  1266. uint64_t now = 0;
  1267. if (manager->system_vtable->get_monotonic_time(&now)) {
  1268. return;
  1269. }
  1270. struct aws_connection_management_transaction work;
  1271. s_aws_connection_management_transaction_init(&work, manager);
  1272. aws_mutex_lock(&manager->lock);
  1273. /* Only if we're not shutting down */
  1274. if (manager->state == AWS_HCMST_READY) {
  1275. const struct aws_linked_list_node *end = aws_linked_list_end(&manager->idle_connections);
  1276. struct aws_linked_list_node *current_node = aws_linked_list_begin(&manager->idle_connections);
  1277. while (current_node != end) {
  1278. struct aws_linked_list_node *node = current_node;
  1279. struct aws_idle_connection *current_idle_connection =
  1280. AWS_CONTAINER_OF(node, struct aws_idle_connection, node);
  1281. if (current_idle_connection->cull_timestamp > now) {
  1282. break;
  1283. }
  1284. current_node = aws_linked_list_next(current_node);
  1285. aws_linked_list_remove(node);
  1286. aws_linked_list_push_back(&work.connections_to_release, node);
  1287. --manager->idle_connection_count;
  1288. AWS_LOGF_DEBUG(
  1289. AWS_LS_HTTP_CONNECTION_MANAGER,
  1290. "id=%p: culling idle connection (%p)",
  1291. (void *)manager,
  1292. (void *)current_idle_connection->connection);
  1293. }
  1294. }
  1295. s_aws_http_connection_manager_get_snapshot(manager, &work.snapshot);
  1296. aws_mutex_unlock(&manager->lock);
  1297. s_aws_http_connection_manager_execute_transaction(&work);
  1298. }
  1299. static void s_cull_task(struct aws_task *task, void *arg, enum aws_task_status status) {
  1300. (void)task;
  1301. if (status != AWS_TASK_STATUS_RUN_READY) {
  1302. return;
  1303. }
  1304. struct aws_http_connection_manager *manager = arg;
  1305. s_cull_idle_connections(manager);
  1306. s_schedule_connection_culling(manager);
  1307. }
  1308. void aws_http_connection_manager_fetch_metrics(
  1309. const struct aws_http_connection_manager *manager,
  1310. struct aws_http_manager_metrics *out_metrics) {
  1311. AWS_PRECONDITION(manager);
  1312. AWS_PRECONDITION(out_metrics);
  1313. AWS_FATAL_ASSERT(aws_mutex_lock((struct aws_mutex *)(void *)&manager->lock) == AWS_OP_SUCCESS);
  1314. out_metrics->available_concurrency = manager->idle_connection_count;
  1315. out_metrics->pending_concurrency_acquires = manager->pending_acquisition_count;
  1316. out_metrics->leased_concurrency = manager->internal_ref[AWS_HCMCT_VENDED_CONNECTION];
  1317. AWS_FATAL_ASSERT(aws_mutex_unlock((struct aws_mutex *)(void *)&manager->lock) == AWS_OP_SUCCESS);
  1318. }