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