1#include "config.h"
2#include "NetworkResourceLoadScheduler.h"
3
4#include "HostRecord.h"
5#include "Logging.h"
6#include "NetworkProcess.h"
7#include "NetworkResourceLoadParameters.h"
8#include "NetworkResourceLoader.h"
9#include <wtf/MainThread.h>
10#include <wtf/text/CString.h>
11
12#if ENABLE(NETWORK_PROCESS)
13
14using namespace WebCore;
15
16namespace WebKit {
17
18static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20;
19
20NetworkResourceLoadScheduler::NetworkResourceLoadScheduler()
21    : m_nonHTTPProtocolHost(HostRecord::create(String(), maxRequestsInFlightForNonHTTPProtocols))
22    , m_requestTimer(this, &NetworkResourceLoadScheduler::requestTimerFired)
23
24{
25    platformInitializeMaximumHTTPConnectionCountPerHost();
26}
27
28void NetworkResourceLoadScheduler::scheduleServePendingRequests()
29{
30    if (!m_requestTimer.isActive())
31        m_requestTimer.startOneShot(0);
32}
33
34void NetworkResourceLoadScheduler::requestTimerFired(WebCore::Timer<NetworkResourceLoadScheduler>*)
35{
36    servePendingRequests();
37}
38
39void NetworkResourceLoadScheduler::scheduleLoader(PassRefPtr<NetworkResourceLoader> loader)
40{
41    ResourceLoadPriority priority = loader->priority();
42    const ResourceRequest& resourceRequest = loader->request();
43
44    LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::scheduleLoader resource '%s'", resourceRequest.url().string().utf8().data());
45
46    HostRecord* host = hostForURL(resourceRequest.url(), CreateIfNotFound);
47    bool hadRequests = host->hasRequests();
48    host->scheduleResourceLoader(loader);
49
50    if (priority > ResourceLoadPriorityLow || !resourceRequest.url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) {
51        // Try to request important resources immediately.
52        host->servePendingRequests(priority);
53        return;
54    }
55
56    // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones.
57    scheduleServePendingRequests();
58}
59
60HostRecord* NetworkResourceLoadScheduler::hostForURL(const WebCore::URL& url, CreateHostPolicy createHostPolicy)
61{
62    if (!url.protocolIsInHTTPFamily())
63        return m_nonHTTPProtocolHost.get();
64
65    m_hosts.checkConsistency();
66    String hostName = url.host();
67    HostRecord* host = m_hosts.get(hostName);
68    if (!host && createHostPolicy == CreateIfNotFound) {
69        RefPtr<HostRecord> newHost = HostRecord::create(hostName, m_maxRequestsInFlightPerHost);
70        host = newHost.get();
71        m_hosts.add(hostName, newHost.release());
72    }
73
74    return host;
75}
76
77void NetworkResourceLoadScheduler::removeLoader(NetworkResourceLoader* loader)
78{
79    ASSERT(RunLoop::isMain());
80    ASSERT(loader);
81
82    LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::removeLoadIdentifier removing loader %s", loader->request().url().string().utf8().data());
83
84    HostRecord* host = loader->hostRecord();
85
86    // Due to a race condition the WebProcess might have messaged the NetworkProcess to remove this identifier
87    // after the NetworkProcess has already removed it internally.
88    // In this situation we might not have a HostRecord to clean up.
89    if (host)
90        host->removeLoader(loader);
91
92    scheduleServePendingRequests();
93}
94
95void NetworkResourceLoadScheduler::receivedRedirect(NetworkResourceLoader* loader, const WebCore::URL& redirectURL)
96{
97    ASSERT(RunLoop::isMain());
98    LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::receivedRedirect loader originally for '%s' redirected to '%s'", loader->request().url().string().utf8().data(), redirectURL.string().utf8().data());
99
100    HostRecord* oldHost = loader->hostRecord();
101
102    // The load may have been cancelled while the message was in flight from network thread to main thread.
103    if (!oldHost)
104        return;
105
106    HostRecord* newHost = hostForURL(redirectURL, CreateIfNotFound);
107
108    if (oldHost->name() == newHost->name())
109        return;
110
111    oldHost->removeLoader(loader);
112    newHost->addLoaderInProgress(loader);
113}
114
115void NetworkResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority)
116{
117    LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::servePendingRequests Serving requests for up to %i hosts with minimum priority %i", m_hosts.size(), minimumPriority);
118
119    m_requestTimer.stop();
120
121    m_nonHTTPProtocolHost->servePendingRequests(minimumPriority);
122
123    m_hosts.checkConsistency();
124    Vector<RefPtr<HostRecord>> hostsToServe;
125    copyValuesToVector(m_hosts, hostsToServe);
126
127    size_t size = hostsToServe.size();
128    for (size_t i = 0; i < size; ++i) {
129        HostRecord* host = hostsToServe[i].get();
130        if (host->hasRequests())
131            host->servePendingRequests(minimumPriority);
132        else
133            m_hosts.remove(host->name());
134    }
135}
136
137static bool removeScheduledLoadersCalled = false;
138
139void NetworkResourceLoadScheduler::removeScheduledLoaders(void* context)
140{
141    ASSERT(RunLoop::isMain());
142    ASSERT(removeScheduledLoadersCalled);
143
144    NetworkResourceLoadScheduler* scheduler = static_cast<NetworkResourceLoadScheduler*>(context);
145    scheduler->removeScheduledLoaders();
146}
147
148void NetworkResourceLoadScheduler::removeScheduledLoaders()
149{
150    Vector<RefPtr<NetworkResourceLoader>> loadersToRemove;
151    {
152        MutexLocker locker(m_loadersToRemoveMutex);
153        loadersToRemove = m_loadersToRemove;
154        m_loadersToRemove.clear();
155        removeScheduledLoadersCalled = false;
156    }
157
158    for (size_t i = 0; i < loadersToRemove.size(); ++i)
159        removeLoader(loadersToRemove[i].get());
160}
161
162void NetworkResourceLoadScheduler::scheduleRemoveLoader(NetworkResourceLoader* loader)
163{
164    MutexLocker locker(m_loadersToRemoveMutex);
165
166    m_loadersToRemove.append(loader);
167
168    if (!removeScheduledLoadersCalled) {
169        removeScheduledLoadersCalled = true;
170        callOnMainThread(NetworkResourceLoadScheduler::removeScheduledLoaders, this);
171    }
172}
173
174uint64_t NetworkResourceLoadScheduler::hostsPendingCount() const
175{
176    uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount() ? 1 : 0;
177
178    HostMap::const_iterator end = m_hosts.end();
179    for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) {
180        if (i->value->pendingRequestCount())
181            ++count;
182    }
183
184    return count;
185}
186
187uint64_t NetworkResourceLoadScheduler::loadsPendingCount() const
188{
189    uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount();
190
191    HostMap::const_iterator end = m_hosts.end();
192    for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i)
193        count += i->value->pendingRequestCount();
194
195    return count;
196}
197
198uint64_t NetworkResourceLoadScheduler::hostsActiveCount() const
199{
200    uint64_t count = 0;
201
202    if (m_nonHTTPProtocolHost->activeLoadCount())
203        count = 1;
204
205    HostMap::const_iterator end = m_hosts.end();
206    for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) {
207        if (i->value->activeLoadCount())
208            ++count;
209    }
210
211    return count;
212}
213
214uint64_t NetworkResourceLoadScheduler::loadsActiveCount() const
215{
216    uint64_t count = m_nonHTTPProtocolHost->activeLoadCount();
217
218    HostMap::const_iterator end = m_hosts.end();
219    for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i)
220        count += i->value->activeLoadCount();
221
222    return count;
223}
224
225} // namespace WebKit
226
227#endif // ENABLE(NETWORK_PROCESS)
228