ConnectionCache.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /*
  2. * Copyright (c) 2021-2022, Ali Mohammad Pur <mpfard@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "ConnectionCache.h"
  7. #include <AK/Debug.h>
  8. #include <AK/Find.h>
  9. #include <LibCore/EventLoop.h>
  10. namespace RequestServer::ConnectionCache {
  11. HashMap<ConnectionKey, NonnullOwnPtr<Vector<NonnullOwnPtr<Connection<Core::TCPSocket, Core::Socket>>>>> g_tcp_connection_cache {};
  12. HashMap<ConnectionKey, NonnullOwnPtr<Vector<NonnullOwnPtr<Connection<TLS::TLSv12>>>>> g_tls_connection_cache {};
  13. void request_did_finish(URL const& url, Core::Socket const* socket)
  14. {
  15. if (!socket) {
  16. dbgln("Request with a null socket finished for URL {}", url);
  17. return;
  18. }
  19. dbgln_if(REQUESTSERVER_DEBUG, "Request for {} finished", url);
  20. ConnectionKey partial_key { url.host(), url.port_or_default() };
  21. auto fire_off_next_job = [&](auto& cache) {
  22. auto it = find_if(cache.begin(), cache.end(), [&](auto& connection) { return connection.key.hostname == partial_key.hostname && connection.key.port == partial_key.port; });
  23. if (it == cache.end()) {
  24. dbgln("Request for URL {} finished, but we don't own that!", url);
  25. return;
  26. }
  27. auto connection_it = it->value->find_if([&](auto& connection) { return connection->socket == socket; });
  28. if (connection_it.is_end()) {
  29. dbgln("Request for URL {} finished, but we don't have a socket for that!", url);
  30. return;
  31. }
  32. auto& connection = *connection_it;
  33. if (connection->request_queue.is_empty()) {
  34. Core::deferred_invoke([&connection, &cache_entry = *it->value, key = it->key, &cache] {
  35. connection->socket->set_notifications_enabled(false);
  36. connection->has_started = false;
  37. connection->current_url = {};
  38. connection->job_data = {};
  39. connection->removal_timer->on_timeout = [ptr = connection.ptr(), &cache_entry, key = move(key), &cache]() mutable {
  40. Core::deferred_invoke([&, key = move(key), ptr] {
  41. dbgln_if(REQUESTSERVER_DEBUG, "Removing no-longer-used connection {} (socket {})", ptr, ptr->socket);
  42. auto did_remove = cache_entry.remove_first_matching([&](auto& entry) { return entry == ptr; });
  43. VERIFY(did_remove);
  44. if (cache_entry.is_empty())
  45. cache.remove(key);
  46. });
  47. };
  48. connection->removal_timer->start();
  49. });
  50. } else {
  51. if (auto result = recreate_socket_if_needed(*connection, url); result.is_error()) {
  52. dbgln("ConnectionCache request finish handler, reconnection failed with {}", result.error());
  53. connection->job_data.fail(Core::NetworkJob::Error::ConnectionFailed);
  54. return;
  55. }
  56. Core::deferred_invoke([&, url] {
  57. dbgln_if(REQUESTSERVER_DEBUG, "Running next job in queue for connection {} @{}", &connection, connection->socket);
  58. connection->timer.start();
  59. connection->current_url = url;
  60. connection->job_data = connection->request_queue.take_first();
  61. connection->socket->set_notifications_enabled(true);
  62. connection->job_data.start(*connection->socket);
  63. });
  64. }
  65. };
  66. if (is<Core::BufferedSocket<TLS::TLSv12>>(socket))
  67. fire_off_next_job(g_tls_connection_cache);
  68. else if (is<Core::BufferedSocket<Core::Socket>>(socket))
  69. fire_off_next_job(g_tcp_connection_cache);
  70. else
  71. dbgln("Unknown socket {} finished for URL {}", socket, url);
  72. }
  73. void dump_jobs()
  74. {
  75. dbgln("=========== TLS Connection Cache ==========");
  76. for (auto& connection : g_tls_connection_cache) {
  77. dbgln(" - {}:{}", connection.key.hostname, connection.key.port);
  78. for (auto& entry : *connection.value) {
  79. dbgln(" - Connection {} (started={}) (socket={})", &entry, entry->has_started, entry->socket);
  80. dbgln(" Currently loading {} ({} elapsed)", entry->current_url, entry->timer.is_valid() ? entry->timer.elapsed() : 0);
  81. dbgln(" Request Queue:");
  82. for (auto& job : entry->request_queue)
  83. dbgln(" - {}", &job);
  84. }
  85. }
  86. dbgln("=========== TCP Connection Cache ==========");
  87. for (auto& connection : g_tcp_connection_cache) {
  88. dbgln(" - {}:{}", connection.key.hostname, connection.key.port);
  89. for (auto& entry : *connection.value) {
  90. dbgln(" - Connection {} (started={}) (socket={})", &entry, entry->has_started, entry->socket);
  91. dbgln(" Currently loading {} ({} elapsed)", entry->current_url, entry->timer.is_valid() ? entry->timer.elapsed() : 0);
  92. dbgln(" Request Queue:");
  93. for (auto& job : entry->request_queue)
  94. dbgln(" - {}", &job);
  95. }
  96. }
  97. }
  98. }