3be648c00c14ad140008257dc8363613efe545e3
[WebKit-https.git] / Source / WebKit / NetworkProcess / soup / NetworkDataTaskSoup.cpp
1 /*
2  * Copyright (C) 2016 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "NetworkDataTaskSoup.h"
28
29 #include "AuthenticationChallengeDisposition.h"
30 #include "AuthenticationManager.h"
31 #include "DataReference.h"
32 #include "Download.h"
33 #include "NetworkLoad.h"
34 #include "NetworkProcess.h"
35 #include "NetworkSessionSoup.h"
36 #include "WebErrors.h"
37 #include <WebCore/AuthenticationChallenge.h>
38 #include <WebCore/HTTPParsers.h>
39 #include <WebCore/MIMETypeRegistry.h>
40 #include <WebCore/NetworkStorageSession.h>
41 #include <WebCore/SharedBuffer.h>
42 #include <WebCore/SoupNetworkSession.h>
43 #include <WebCore/TextEncoding.h>
44 #include <wtf/MainThread.h>
45 #include <wtf/glib/RunLoopSourcePriority.h>
46
47 namespace WebKit {
48 using namespace WebCore;
49
50 static const size_t gDefaultReadBufferSize = 8192;
51
52 NetworkDataTaskSoup::NetworkDataTaskSoup(NetworkSession& session, NetworkDataTaskClient& client, const ResourceRequest& requestWithCredentials, StoredCredentialsPolicy storedCredentialsPolicy, ContentSniffingPolicy shouldContentSniff, WebCore::ContentEncodingSniffingPolicy, bool shouldClearReferrerOnHTTPSToHTTPRedirect, bool dataTaskIsForMainFrameNavigation)
53     : NetworkDataTask(session, client, requestWithCredentials, storedCredentialsPolicy, shouldClearReferrerOnHTTPSToHTTPRedirect, dataTaskIsForMainFrameNavigation)
54     , m_shouldContentSniff(shouldContentSniff)
55     , m_timeoutSource(RunLoop::main(), this, &NetworkDataTaskSoup::timeoutFired)
56 {
57     m_session->registerNetworkDataTask(*this);
58     if (m_scheduledFailureType != NoFailure)
59         return;
60
61     auto request = requestWithCredentials;
62     if (request.url().protocolIsInHTTPFamily()) {
63         m_startTime = MonotonicTime::now();
64         auto url = request.url();
65         if (m_storedCredentialsPolicy == StoredCredentialsPolicy::Use) {
66             m_user = url.user();
67             m_password = url.pass();
68             request.removeCredentials();
69
70             if (m_user.isEmpty() && m_password.isEmpty())
71                 m_initialCredential = m_session->networkStorageSession()->credentialStorage().get(m_partition, request.url());
72             else
73                 m_session->networkStorageSession()->credentialStorage().set(m_partition, Credential(m_user, m_password, CredentialPersistenceNone), request.url());
74         }
75         applyAuthenticationToRequest(request);
76     }
77     createRequest(WTFMove(request));
78 }
79
80 NetworkDataTaskSoup::~NetworkDataTaskSoup()
81 {
82     clearRequest();
83     m_session->unregisterNetworkDataTask(*this);
84 }
85
86 String NetworkDataTaskSoup::suggestedFilename() const
87 {
88     if (!m_suggestedFilename.isEmpty())
89         return m_suggestedFilename;
90
91     String suggestedFilename = m_response.suggestedFilename();
92     if (!suggestedFilename.isEmpty())
93         return suggestedFilename;
94
95     return decodeURLEscapeSequences(m_response.url().lastPathComponent());
96 }
97
98 void NetworkDataTaskSoup::setPendingDownloadLocation(const String& filename, SandboxExtension::Handle&& sandboxExtensionHandle, bool allowOverwrite)
99 {
100     NetworkDataTask::setPendingDownloadLocation(filename, WTFMove(sandboxExtensionHandle), allowOverwrite);
101     m_allowOverwriteDownload = allowOverwrite;
102 }
103
104 void NetworkDataTaskSoup::createRequest(ResourceRequest&& request)
105 {
106     m_currentRequest = WTFMove(request);
107
108     GUniquePtr<SoupURI> soupURI = m_currentRequest.createSoupURI();
109     if (!soupURI) {
110         scheduleFailure(InvalidURLFailure);
111         return;
112     }
113
114     GRefPtr<SoupRequest> soupRequest = adoptGRef(soup_session_request_uri(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), soupURI.get(), nullptr));
115     if (!soupRequest) {
116         scheduleFailure(InvalidURLFailure);
117         return;
118     }
119
120     m_currentRequest.updateSoupRequest(soupRequest.get());
121
122     if (!m_currentRequest.url().protocolIsInHTTPFamily()) {
123         m_soupRequest = WTFMove(soupRequest);
124         return;
125     }
126
127     // HTTP request.
128     GRefPtr<SoupMessage> soupMessage = adoptGRef(soup_request_http_get_message(SOUP_REQUEST_HTTP(soupRequest.get())));
129     if (!soupMessage) {
130         scheduleFailure(InvalidURLFailure);
131         return;
132     }
133
134     unsigned messageFlags = SOUP_MESSAGE_NO_REDIRECT;
135
136     m_currentRequest.updateSoupMessage(soupMessage.get());
137     if (m_shouldContentSniff == ContentSniffingPolicy::DoNotSniffContent)
138         soup_message_disable_feature(soupMessage.get(), SOUP_TYPE_CONTENT_SNIFFER);
139     if (m_user.isEmpty() && m_password.isEmpty() && m_storedCredentialsPolicy == StoredCredentialsPolicy::DoNotUse) {
140 #if SOUP_CHECK_VERSION(2, 57, 1)
141         messageFlags |= SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE;
142 #else
143         // In case credential is not available and credential storage should not to be used,
144         // disable authentication manager so that credentials stored in libsoup are not used.
145         soup_message_disable_feature(soupMessage.get(), SOUP_TYPE_AUTH_MANAGER);
146 #endif
147     }
148
149     // Make sure we have an Accept header for subresources; some sites want this to serve some of their subresources.
150     if (!soup_message_headers_get_one(soupMessage->request_headers, "Accept"))
151         soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*");
152
153     // In the case of XHR .send() and .send("") explicitly tell libsoup to send a zero content-lenght header
154     // for consistency with other UA implementations like Firefox. It's done in the backend here instead of
155     // in XHR code since in XHR CORS checking prevents us from this kind of late header manipulation.
156     if ((soupMessage->method == SOUP_METHOD_POST || soupMessage->method == SOUP_METHOD_PUT) && !soupMessage->request_body->length)
157         soup_message_headers_set_content_length(soupMessage->request_headers, 0);
158
159     soup_message_set_flags(soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(soupMessage.get()) | messageFlags));
160
161 #if SOUP_CHECK_VERSION(2, 43, 1)
162     soup_message_set_priority(soupMessage.get(), toSoupMessagePriority(m_currentRequest.priority()));
163 #endif
164
165     m_soupRequest = WTFMove(soupRequest);
166     m_soupMessage = WTFMove(soupMessage);
167
168     g_signal_connect(m_soupMessage.get(), "got-headers", G_CALLBACK(gotHeadersCallback), this);
169     g_signal_connect(m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), this);
170     g_signal_connect(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), "authenticate",  G_CALLBACK(authenticateCallback), this);
171     g_signal_connect(m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), this);
172     g_signal_connect(m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), this);
173 #if SOUP_CHECK_VERSION(2, 49, 91)
174     g_signal_connect(m_soupMessage.get(), "starting", G_CALLBACK(startingCallback), this);
175 #else
176     g_signal_connect(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), "request-started", G_CALLBACK(requestStartedCallback), this);
177 #endif
178 }
179
180 void NetworkDataTaskSoup::clearRequest()
181 {
182     if (m_state == State::Completed)
183         return;
184
185     m_state = State::Completed;
186
187     stopTimeout();
188     m_pendingResult = nullptr;
189     m_soupRequest = nullptr;
190     m_inputStream = nullptr;
191     m_multipartInputStream = nullptr;
192     m_downloadOutputStream = nullptr;
193     g_cancellable_cancel(m_cancellable.get());
194     m_cancellable = nullptr;
195     if (m_soupMessage) {
196         g_signal_handlers_disconnect_matched(m_soupMessage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
197         soup_session_cancel_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get(), SOUP_STATUS_CANCELLED);
198         m_soupMessage = nullptr;
199     }
200     g_signal_handlers_disconnect_matched(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
201 }
202
203 void NetworkDataTaskSoup::resume()
204 {
205     ASSERT(m_state != State::Running);
206     if (m_state == State::Canceling || m_state == State::Completed)
207         return;
208
209     m_state = State::Running;
210
211     if (m_scheduledFailureType != NoFailure) {
212         ASSERT(m_failureTimer.isActive());
213         return;
214     }
215
216     startTimeout();
217
218     RefPtr<NetworkDataTaskSoup> protectedThis(this);
219     if (m_soupRequest && !m_cancellable) {
220         m_cancellable = adoptGRef(g_cancellable_new());
221         soup_request_send_async(m_soupRequest.get(), m_cancellable.get(), reinterpret_cast<GAsyncReadyCallback>(sendRequestCallback), protectedThis.leakRef());
222         return;
223     }
224
225     if (m_pendingResult) {
226         GRefPtr<GAsyncResult> pendingResult = WTFMove(m_pendingResult);
227         if (m_inputStream)
228             readCallback(m_inputStream.get(), pendingResult.get(), protectedThis.leakRef());
229         else if (m_multipartInputStream)
230             requestNextPartCallback(m_multipartInputStream.get(), pendingResult.get(), protectedThis.leakRef());
231         else if (m_soupRequest)
232             sendRequestCallback(m_soupRequest.get(), pendingResult.get(), protectedThis.leakRef());
233         else
234             ASSERT_NOT_REACHED();
235     }
236 }
237
238 void NetworkDataTaskSoup::cancel()
239 {
240     if (m_state == State::Canceling || m_state == State::Completed)
241         return;
242
243     m_state = State::Canceling;
244
245     if (m_soupMessage)
246         soup_session_cancel_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get(), SOUP_STATUS_CANCELLED);
247
248     g_cancellable_cancel(m_cancellable.get());
249
250     if (isDownload())
251         cleanDownloadFiles();
252 }
253
254 void NetworkDataTaskSoup::invalidateAndCancel()
255 {
256     cancel();
257     clearRequest();
258 }
259
260 NetworkDataTask::State NetworkDataTaskSoup::state() const
261 {
262     return m_state;
263 }
264
265 void NetworkDataTaskSoup::timeoutFired()
266 {
267     if (m_state == State::Canceling || m_state == State::Completed || !m_client) {
268         clearRequest();
269         return;
270     }
271
272     RefPtr<NetworkDataTaskSoup> protectedThis(this);
273     invalidateAndCancel();
274     dispatchDidCompleteWithError(ResourceError::timeoutError(m_firstRequest.url()));
275 }
276
277 void NetworkDataTaskSoup::startTimeout()
278 {
279     if (m_firstRequest.timeoutInterval() > 0)
280         m_timeoutSource.startOneShot(1_s * m_firstRequest.timeoutInterval());
281 }
282
283 void NetworkDataTaskSoup::stopTimeout()
284 {
285     m_timeoutSource.stop();
286 }
287
288 void NetworkDataTaskSoup::sendRequestCallback(SoupRequest* soupRequest, GAsyncResult* result, NetworkDataTaskSoup* task)
289 {
290     RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
291     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
292         task->clearRequest();
293         return;
294     }
295     ASSERT(soupRequest == task->m_soupRequest.get());
296
297     if (task->state() == State::Suspended) {
298         ASSERT(!task->m_pendingResult);
299         task->m_pendingResult = result;
300         return;
301     }
302
303     GUniqueOutPtr<GError> error;
304     GRefPtr<GInputStream> inputStream = adoptGRef(soup_request_send_finish(soupRequest, result, &error.outPtr()));
305     if (error)
306         task->didFail(ResourceError::httpError(task->m_soupMessage.get(), error.get(), soupRequest));
307     else
308         task->didSendRequest(WTFMove(inputStream));
309 }
310
311 void NetworkDataTaskSoup::didSendRequest(GRefPtr<GInputStream>&& inputStream)
312 {
313     if (m_soupMessage) {
314         if (m_shouldContentSniff == ContentSniffingPolicy::SniffContent && m_soupMessage->status_code != SOUP_STATUS_NOT_MODIFIED)
315             m_response.setSniffedContentType(soup_request_get_content_type(m_soupRequest.get()));
316         m_response.updateFromSoupMessage(m_soupMessage.get());
317         if (m_response.mimeType().isEmpty() && m_soupMessage->status_code != SOUP_STATUS_NOT_MODIFIED)
318             m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(m_response.url().path()));
319
320         if (shouldStartHTTPRedirection()) {
321             m_inputStream = WTFMove(inputStream);
322             skipInputStreamForRedirection();
323             return;
324         }
325
326         if (m_response.isMultipart())
327             m_multipartInputStream = adoptGRef(soup_multipart_input_stream_new(m_soupMessage.get(), inputStream.get()));
328         else
329             m_inputStream = WTFMove(inputStream);
330
331         m_networkLoadMetrics.responseStart = MonotonicTime::now() - m_startTime;
332     } else {
333         m_response.setURL(m_firstRequest.url());
334         const gchar* contentType = soup_request_get_content_type(m_soupRequest.get());
335         m_response.setMimeType(extractMIMETypeFromMediaType(contentType));
336         m_response.setTextEncodingName(extractCharsetFromMediaType(contentType));
337         m_response.setExpectedContentLength(soup_request_get_content_length(m_soupRequest.get()));
338         if (m_response.mimeType().isEmpty())
339             m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(m_response.url().path()));
340
341         m_inputStream = WTFMove(inputStream);
342     }
343
344     dispatchDidReceiveResponse();
345 }
346
347 void NetworkDataTaskSoup::dispatchDidReceiveResponse()
348 {
349     ASSERT(!m_response.isNull());
350
351     // FIXME: Remove this once nobody depends on deprecatedNetworkLoadMetrics.
352     NetworkLoadMetrics& deprecatedResponseMetrics = m_response.deprecatedNetworkLoadMetrics();
353     deprecatedResponseMetrics.responseStart = m_networkLoadMetrics.responseStart;
354     deprecatedResponseMetrics.domainLookupStart = m_networkLoadMetrics.domainLookupStart;
355     deprecatedResponseMetrics.domainLookupEnd = m_networkLoadMetrics.domainLookupEnd;
356     deprecatedResponseMetrics.connectStart = m_networkLoadMetrics.connectStart;
357     deprecatedResponseMetrics.secureConnectionStart = m_networkLoadMetrics.secureConnectionStart;
358     deprecatedResponseMetrics.connectEnd = m_networkLoadMetrics.connectEnd;
359     deprecatedResponseMetrics.requestStart = m_networkLoadMetrics.requestStart;
360     deprecatedResponseMetrics.responseStart = m_networkLoadMetrics.responseStart;
361
362     didReceiveResponse(ResourceResponse(m_response), [this, protectedThis = makeRef(*this)](PolicyAction policyAction) {
363         if (m_state == State::Canceling || m_state == State::Completed) {
364             clearRequest();
365             return;
366         }
367
368         switch (policyAction) {
369         case PolicyAction::Use:
370             if (m_inputStream)
371                 read();
372             else if (m_multipartInputStream)
373                 requestNextPart();
374             else
375                 ASSERT_NOT_REACHED();
376
377             break;
378         case PolicyAction::Ignore:
379             clearRequest();
380             break;
381         case PolicyAction::Download:
382             download();
383             break;
384         case PolicyAction::StopAllLoads:
385             ASSERT_NOT_REACHED();
386             break;
387         }
388     });
389 }
390
391 void NetworkDataTaskSoup::dispatchDidCompleteWithError(const ResourceError& error)
392 {
393     m_networkLoadMetrics.responseEnd = MonotonicTime::now() - m_startTime;
394     m_networkLoadMetrics.markComplete();
395
396     m_client->didCompleteWithError(error, m_networkLoadMetrics);
397 }
398
399 gboolean NetworkDataTaskSoup::tlsConnectionAcceptCertificateCallback(GTlsConnection* connection, GTlsCertificate* certificate, GTlsCertificateFlags errors, NetworkDataTaskSoup* task)
400 {
401     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
402         task->clearRequest();
403         return FALSE;
404     }
405
406     auto* connectionMessage = g_object_get_data(G_OBJECT(connection), "wk-soup-message");
407     if (connectionMessage != task->m_soupMessage.get())
408         return FALSE;
409
410     return task->tlsConnectionAcceptCertificate(certificate, errors);
411 }
412
413 bool NetworkDataTaskSoup::tlsConnectionAcceptCertificate(GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors)
414 {
415     ASSERT(m_soupRequest);
416     URL url = soupURIToURL(soup_request_get_uri(m_soupRequest.get()));
417     auto error = SoupNetworkSession::checkTLSErrors(url, certificate, tlsErrors);
418     if (!error)
419         return true;
420
421     RefPtr<NetworkDataTaskSoup> protectedThis(this);
422     invalidateAndCancel();
423     dispatchDidCompleteWithError(error.value());
424     return false;
425 }
426
427 void NetworkDataTaskSoup::applyAuthenticationToRequest(ResourceRequest& request)
428 {
429     if (m_user.isEmpty() && m_password.isEmpty())
430         return;
431
432     auto url = request.url();
433     url.setUser(m_user);
434     url.setPass(m_password);
435     request.setURL(url);
436
437     m_user = String();
438     m_password = String();
439 }
440
441 void NetworkDataTaskSoup::authenticateCallback(SoupSession* session, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying, NetworkDataTaskSoup* task)
442 {
443     ASSERT(session == static_cast<NetworkSessionSoup&>(task->m_session.get()).soupSession());
444
445     // We don't return early here in case the given soupMessage is different to m_soupMessage when
446     // it's proxy authentication and the request URL is HTTPS, because in that case libsoup uses a
447     // tunnel internally and the SoupMessage used for the authentication is the tunneling one.
448     // See https://bugs.webkit.org/show_bug.cgi?id=175378.
449     if (soupMessage != task->m_soupMessage.get() && (soupMessage->status_code != SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || !task->m_currentRequest.url().protocolIs("https")))
450         return;
451
452     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
453         task->clearRequest();
454         return;
455     }
456
457     task->authenticate(AuthenticationChallenge(soupMessage, soupAuth, retrying));
458 }
459
460 static inline bool isAuthenticationFailureStatusCode(int httpStatusCode)
461 {
462     return httpStatusCode == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || httpStatusCode == SOUP_STATUS_UNAUTHORIZED;
463 }
464
465 void NetworkDataTaskSoup::authenticate(AuthenticationChallenge&& challenge)
466 {
467     ASSERT(m_soupMessage);
468     if (m_storedCredentialsPolicy == StoredCredentialsPolicy::Use) {
469         if (!m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
470             // The stored credential wasn't accepted, stop using it. There is a race condition
471             // here, since a different credential might have already been stored by another
472             // NetworkDataTask, but the observable effect should be very minor, if any.
473             m_session->networkStorageSession()->credentialStorage().remove(m_partition, challenge.protectionSpace());
474         }
475
476         if (!challenge.previousFailureCount()) {
477             auto credential = m_session->networkStorageSession()->credentialStorage().get(m_partition, challenge.protectionSpace());
478             if (!credential.isEmpty() && credential != m_initialCredential) {
479                 ASSERT(credential.persistence() == CredentialPersistenceNone);
480
481                 if (isAuthenticationFailureStatusCode(challenge.failureResponse().httpStatusCode())) {
482                     // Store the credential back, possibly adding it as a default for this directory.
483                     m_session->networkStorageSession()->credentialStorage().set(m_partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
484                 }
485                 soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
486                 return;
487             }
488         }
489     }
490
491     soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
492
493     // We could also do this before we even start the request, but that would be at the expense
494     // of all request latency, versus a one-time latency for the small subset of requests that
495     // use HTTP authentication. In the end, this doesn't matter much, because persistent credentials
496     // will become session credentials after the first use.
497     if (m_storedCredentialsPolicy == StoredCredentialsPolicy::Use) {
498         auto protectionSpace = challenge.protectionSpace();
499         m_session->networkStorageSession()->getCredentialFromPersistentStorage(protectionSpace, m_cancellable.get(),
500             [this, protectedThis = makeRef(*this), authChallenge = WTFMove(challenge)] (Credential&& credential) mutable {
501                 if (m_state == State::Canceling || m_state == State::Completed || !m_client) {
502                     clearRequest();
503                     return;
504                 }
505
506                 authChallenge.setProposedCredential(WTFMove(credential));
507                 continueAuthenticate(WTFMove(authChallenge));
508         });
509     } else
510         continueAuthenticate(WTFMove(challenge));
511 }
512
513 void NetworkDataTaskSoup::continueAuthenticate(AuthenticationChallenge&& challenge)
514 {
515     m_client->didReceiveChallenge(AuthenticationChallenge(challenge), [this, protectedThis = makeRef(*this), challenge](AuthenticationChallengeDisposition disposition, const Credential& credential) {
516         if (m_state == State::Canceling || m_state == State::Completed) {
517             clearRequest();
518             return;
519         }
520
521         if (disposition == AuthenticationChallengeDisposition::Cancel) {
522             cancel();
523             didFail(cancelledError(m_soupRequest.get()));
524             return;
525         }
526
527         if (disposition == AuthenticationChallengeDisposition::UseCredential && !credential.isEmpty()) {
528             if (m_storedCredentialsPolicy == StoredCredentialsPolicy::Use) {
529                 // Eventually we will manage per-session credentials only internally or use some newly-exposed API from libsoup,
530                 // because once we authenticate via libsoup, there is no way to ignore it for a particular request. Right now,
531                 // we place the credentials in the store even though libsoup will never fire the authenticate signal again for
532                 // this protection space.
533                 if (credential.persistence() == CredentialPersistenceForSession || credential.persistence() == CredentialPersistencePermanent)
534                     m_session->networkStorageSession()->credentialStorage().set(m_partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
535
536                 if (credential.persistence() == CredentialPersistencePermanent) {
537                     m_protectionSpaceForPersistentStorage = challenge.protectionSpace();
538                     m_credentialForPersistentStorage = credential;
539                 }
540             }
541
542             soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
543         }
544
545         soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
546     });
547 }
548
549 void NetworkDataTaskSoup::skipInputStreamForRedirectionCallback(GInputStream* inputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
550 {
551     RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
552     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
553         task->clearRequest();
554         return;
555     }
556     ASSERT(inputStream == task->m_inputStream.get());
557
558     GUniqueOutPtr<GError> error;
559     gssize bytesSkipped = g_input_stream_skip_finish(inputStream, result, &error.outPtr());
560     if (error)
561         task->didFail(ResourceError::genericGError(error.get(), task->m_soupRequest.get()));
562     else if (bytesSkipped > 0)
563         task->skipInputStreamForRedirection();
564     else
565         task->didFinishSkipInputStreamForRedirection();
566 }
567
568 void NetworkDataTaskSoup::skipInputStreamForRedirection()
569 {
570     ASSERT(m_inputStream);
571     RefPtr<NetworkDataTaskSoup> protectedThis(this);
572     g_input_stream_skip_async(m_inputStream.get(), gDefaultReadBufferSize, RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(),
573         reinterpret_cast<GAsyncReadyCallback>(skipInputStreamForRedirectionCallback), protectedThis.leakRef());
574 }
575
576 void NetworkDataTaskSoup::didFinishSkipInputStreamForRedirection()
577 {
578     g_input_stream_close(m_inputStream.get(), nullptr, nullptr);
579     continueHTTPRedirection();
580 }
581
582 static bool shouldRedirectAsGET(SoupMessage* message, bool crossOrigin)
583 {
584     if (message->method == SOUP_METHOD_GET || message->method == SOUP_METHOD_HEAD)
585         return false;
586
587     switch (message->status_code) {
588     case SOUP_STATUS_SEE_OTHER:
589         return true;
590     case SOUP_STATUS_FOUND:
591     case SOUP_STATUS_MOVED_PERMANENTLY:
592         if (message->method == SOUP_METHOD_POST)
593             return true;
594         break;
595     }
596
597     if (crossOrigin && message->method == SOUP_METHOD_DELETE)
598         return true;
599
600     return false;
601 }
602
603 bool NetworkDataTaskSoup::shouldStartHTTPRedirection()
604 {
605     ASSERT(m_soupMessage);
606     ASSERT(!m_response.isNull());
607
608     auto status = m_response.httpStatusCode();
609     if (!SOUP_STATUS_IS_REDIRECTION(status))
610         return false;
611
612     // Some 3xx status codes aren't actually redirects.
613     if (status == 300 || status == 304 || status == 305 || status == 306)
614         return false;
615
616     if (m_response.httpHeaderField(HTTPHeaderName::Location).isEmpty())
617         return false;
618
619     return true;
620 }
621
622 void NetworkDataTaskSoup::continueHTTPRedirection()
623 {
624     ASSERT(m_soupMessage);
625     ASSERT(!m_response.isNull());
626
627     static const unsigned maxRedirects = 20;
628     if (m_redirectCount++ > maxRedirects) {
629         didFail(ResourceError::transportError(m_soupRequest.get(), SOUP_STATUS_TOO_MANY_REDIRECTS, "Too many redirects"));
630         return;
631     }
632
633     ResourceRequest request = m_currentRequest;
634     URL redirectedURL = URL(m_response.url(), m_response.httpHeaderField(HTTPHeaderName::Location));
635     if (!redirectedURL.hasFragmentIdentifier() && request.url().hasFragmentIdentifier())
636         redirectedURL.setFragmentIdentifier(request.url().fragmentIdentifier());
637     request.setURL(redirectedURL);
638
639     // Should not set Referer after a redirect from a secure resource to non-secure one.
640     if (m_shouldClearReferrerOnHTTPSToHTTPRedirect && !request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
641         request.clearHTTPReferrer();
642
643     bool isCrossOrigin = !protocolHostAndPortAreEqual(m_currentRequest.url(), request.url());
644     if (!equalLettersIgnoringASCIICase(request.httpMethod(), "get")) {
645         // Change newRequest method to GET if change was made during a previous redirection or if current redirection says so.
646         if (m_soupMessage->method == SOUP_METHOD_GET || !request.url().protocolIsInHTTPFamily() || shouldRedirectAsGET(m_soupMessage.get(), isCrossOrigin)) {
647             request.setHTTPMethod("GET");
648             request.setHTTPBody(nullptr);
649             request.clearHTTPContentType();
650         }
651     }
652
653     const auto& url = request.url();
654     m_user = url.user();
655     m_password = url.pass();
656     m_lastHTTPMethod = request.httpMethod();
657     request.removeCredentials();
658
659     if (isCrossOrigin) {
660         // The network layer might carry over some headers from the original request that
661         // we want to strip here because the redirect is cross-origin.
662         request.clearHTTPAuthorization();
663         request.clearHTTPOrigin();
664     } else if (url.protocolIsInHTTPFamily() && m_storedCredentialsPolicy == StoredCredentialsPolicy::Use) {
665         if (m_user.isEmpty() && m_password.isEmpty()) {
666             auto credential = m_session->networkStorageSession()->credentialStorage().get(m_partition, request.url());
667             if (!credential.isEmpty())
668                 m_initialCredential = credential;
669         }
670     }
671
672     clearRequest();
673
674     auto response = ResourceResponse(m_response);
675     m_client->willPerformHTTPRedirection(WTFMove(response), WTFMove(request), [this, protectedThis = makeRef(*this), isCrossOrigin](const ResourceRequest& newRequest) {
676         if (newRequest.isNull() || m_state == State::Canceling)
677             return;
678
679         auto request = newRequest;
680         if (request.url().protocolIsInHTTPFamily()) {
681             if (isCrossOrigin) {
682                 m_startTime = MonotonicTime::now();
683                 m_networkLoadMetrics.reset();
684             }
685
686             applyAuthenticationToRequest(request);
687         }
688         createRequest(WTFMove(request));
689         if (m_soupRequest && m_state != State::Suspended) {
690             m_state = State::Suspended;
691             resume();
692         }
693     });
694 }
695
696 void NetworkDataTaskSoup::readCallback(GInputStream* inputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
697 {
698     RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
699     if (task->state() == State::Canceling || task->state() == State::Completed || (!task->m_client && !task->isDownload())) {
700         task->clearRequest();
701         return;
702     }
703     ASSERT(inputStream == task->m_inputStream.get());
704
705     if (task->state() == State::Suspended) {
706         ASSERT(!task->m_pendingResult);
707         task->m_pendingResult = result;
708         return;
709     }
710
711     GUniqueOutPtr<GError> error;
712     gssize bytesRead = g_input_stream_read_finish(inputStream, result, &error.outPtr());
713     if (error)
714         task->didFail(ResourceError::genericGError(error.get(), task->m_soupRequest.get()));
715     else if (bytesRead > 0)
716         task->didRead(bytesRead);
717     else
718         task->didFinishRead();
719 }
720
721 void NetworkDataTaskSoup::read()
722 {
723     RefPtr<NetworkDataTaskSoup> protectedThis(this);
724     ASSERT(m_inputStream);
725     m_readBuffer.grow(gDefaultReadBufferSize);
726     g_input_stream_read_async(m_inputStream.get(), m_readBuffer.data(), m_readBuffer.size(), RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(),
727         reinterpret_cast<GAsyncReadyCallback>(readCallback), protectedThis.leakRef());
728 }
729
730 void NetworkDataTaskSoup::didRead(gssize bytesRead)
731 {
732     m_readBuffer.shrink(bytesRead);
733     if (m_downloadOutputStream) {
734         ASSERT(isDownload());
735         writeDownload();
736     } else {
737         ASSERT(m_client);
738         m_client->didReceiveData(SharedBuffer::create(WTFMove(m_readBuffer)));
739         read();
740     }
741 }
742
743 void NetworkDataTaskSoup::didFinishRead()
744 {
745     ASSERT(m_inputStream);
746     g_input_stream_close(m_inputStream.get(), nullptr, nullptr);
747     m_inputStream = nullptr;
748     if (m_multipartInputStream) {
749         requestNextPart();
750         return;
751     }
752
753     if (m_downloadOutputStream) {
754         didFinishDownload();
755         return;
756     }
757
758     clearRequest();
759     ASSERT(m_client);
760     dispatchDidCompleteWithError({ });
761 }
762
763 void NetworkDataTaskSoup::requestNextPartCallback(SoupMultipartInputStream* multipartInputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
764 {
765     RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
766     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
767         task->clearRequest();
768         return;
769     }
770     ASSERT(multipartInputStream == task->m_multipartInputStream.get());
771
772     if (task->state() == State::Suspended) {
773         ASSERT(!task->m_pendingResult);
774         task->m_pendingResult = result;
775         return;
776     }
777
778     GUniqueOutPtr<GError> error;
779     GRefPtr<GInputStream> inputStream = adoptGRef(soup_multipart_input_stream_next_part_finish(multipartInputStream, result, &error.outPtr()));
780     if (error)
781         task->didFail(ResourceError::httpError(task->m_soupMessage.get(), error.get(), task->m_soupRequest.get()));
782     else if (inputStream)
783         task->didRequestNextPart(WTFMove(inputStream));
784     else
785         task->didFinishRequestNextPart();
786 }
787
788 void NetworkDataTaskSoup::requestNextPart()
789 {
790     RefPtr<NetworkDataTaskSoup> protectedThis(this);
791     ASSERT(m_multipartInputStream);
792     ASSERT(!m_inputStream);
793     soup_multipart_input_stream_next_part_async(m_multipartInputStream.get(), RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(),
794         reinterpret_cast<GAsyncReadyCallback>(requestNextPartCallback), protectedThis.leakRef());
795 }
796
797 void NetworkDataTaskSoup::didRequestNextPart(GRefPtr<GInputStream>&& inputStream)
798 {
799     ASSERT(!m_inputStream);
800     m_inputStream = WTFMove(inputStream);
801     m_response = ResourceResponse();
802     m_response.setURL(m_firstRequest.url());
803     m_response.updateFromSoupMessageHeaders(soup_multipart_input_stream_get_headers(m_multipartInputStream.get()));
804     dispatchDidReceiveResponse();
805 }
806
807 void NetworkDataTaskSoup::didFinishRequestNextPart()
808 {
809     ASSERT(!m_inputStream);
810     ASSERT(m_multipartInputStream);
811     g_input_stream_close(G_INPUT_STREAM(m_multipartInputStream.get()), nullptr, nullptr);
812     clearRequest();
813     dispatchDidCompleteWithError({ });
814 }
815
816 void NetworkDataTaskSoup::gotHeadersCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
817 {
818     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
819         task->clearRequest();
820         return;
821     }
822     ASSERT(task->m_soupMessage.get() == soupMessage);
823     task->didGetHeaders();
824 }
825
826 void NetworkDataTaskSoup::didGetHeaders()
827 {
828     // We are a bit more conservative with the persistent credential storage than the session store,
829     // since we are waiting until we know that this authentication succeeded before actually storing.
830     // This is because we want to avoid hitting the disk twice (once to add and once to remove) for
831     // incorrect credentials or polluting the keychain with invalid credentials.
832     if (!isAuthenticationFailureStatusCode(m_soupMessage->status_code) && m_soupMessage->status_code < 500) {
833         m_session->networkStorageSession()->saveCredentialToPersistentStorage(m_protectionSpaceForPersistentStorage, m_credentialForPersistentStorage);
834         m_protectionSpaceForPersistentStorage = ProtectionSpace();
835         m_credentialForPersistentStorage = Credential();
836     }
837
838     // Soup adds more headers to the request after starting signal is emitted, and got-headers
839     // is the first one we receive after starting, so we use it also to get information about the
840     // request headers.
841     if (shouldCaptureExtraNetworkLoadMetrics()) {
842         HTTPHeaderMap requestHeaders;
843         SoupMessageHeadersIter headersIter;
844         soup_message_headers_iter_init(&headersIter, m_soupMessage->request_headers);
845         const char* headerName;
846         const char* headerValue;
847         while (soup_message_headers_iter_next(&headersIter, &headerName, &headerValue))
848             requestHeaders.set(String(headerName), String(headerValue));
849         m_networkLoadMetrics.requestHeaders = WTFMove(requestHeaders);
850     }
851 }
852
853 void NetworkDataTaskSoup::wroteBodyDataCallback(SoupMessage* soupMessage, SoupBuffer* buffer, NetworkDataTaskSoup* task)
854 {
855     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
856         task->clearRequest();
857         return;
858     }
859     ASSERT(task->m_soupMessage.get() == soupMessage);
860     task->didWriteBodyData(buffer->length);
861 }
862
863 void NetworkDataTaskSoup::didWriteBodyData(uint64_t bytesSent)
864 {
865     RefPtr<NetworkDataTaskSoup> protectedThis(this);
866     m_bodyDataTotalBytesSent += bytesSent;
867     m_client->didSendData(m_bodyDataTotalBytesSent, m_soupMessage->request_body->length);
868 }
869
870 void NetworkDataTaskSoup::download()
871 {
872     ASSERT(isDownload());
873     ASSERT(m_pendingDownloadLocation);
874     ASSERT(!m_response.isNull());
875
876     if (m_response.httpStatusCode() >= 400) {
877         didFailDownload(downloadNetworkError(m_response.url(), m_response.httpStatusText()));
878         return;
879     }
880
881     CString downloadDestinationPath = m_pendingDownloadLocation.utf8();
882     m_downloadDestinationFile = adoptGRef(g_file_new_for_path(downloadDestinationPath.data()));
883     GRefPtr<GFileOutputStream> outputStream;
884     GUniqueOutPtr<GError> error;
885     if (m_allowOverwriteDownload)
886         outputStream = adoptGRef(g_file_replace(m_downloadDestinationFile.get(), nullptr, FALSE, G_FILE_CREATE_NONE, nullptr, &error.outPtr()));
887     else
888         outputStream = adoptGRef(g_file_create(m_downloadDestinationFile.get(), G_FILE_CREATE_NONE, nullptr, &error.outPtr()));
889     if (!outputStream) {
890         didFailDownload(downloadDestinationError(m_response, error->message));
891         return;
892     }
893
894     GUniquePtr<char> intermediatePath(g_strdup_printf("%s.wkdownload", downloadDestinationPath.data()));
895     m_downloadIntermediateFile = adoptGRef(g_file_new_for_path(intermediatePath.get()));
896     outputStream = adoptGRef(g_file_replace(m_downloadIntermediateFile.get(), nullptr, TRUE, G_FILE_CREATE_NONE, nullptr, &error.outPtr()));
897     if (!outputStream) {
898         didFailDownload(downloadDestinationError(m_response, error->message));
899         return;
900     }
901     m_downloadOutputStream = adoptGRef(G_OUTPUT_STREAM(outputStream.leakRef()));
902
903     auto& downloadManager = m_session->networkProcess().downloadManager();
904     auto download = std::make_unique<Download>(downloadManager, m_pendingDownloadID, *this, m_session->sessionID(), suggestedFilename());
905     auto* downloadPtr = download.get();
906     downloadManager.dataTaskBecameDownloadTask(m_pendingDownloadID, WTFMove(download));
907     downloadPtr->didCreateDestination(m_pendingDownloadLocation);
908
909     ASSERT(!m_client);
910     read();
911 }
912
913 void NetworkDataTaskSoup::writeDownloadCallback(GOutputStream* outputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
914 {
915     RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
916     if (task->state() == State::Canceling || task->state() == State::Completed || !task->isDownload()) {
917         task->clearRequest();
918         return;
919     }
920     ASSERT(outputStream == task->m_downloadOutputStream.get());
921
922     GUniqueOutPtr<GError> error;
923     gsize bytesWritten;
924 #if GLIB_CHECK_VERSION(2, 44, 0)
925     g_output_stream_write_all_finish(outputStream, result, &bytesWritten, &error.outPtr());
926 #else
927     gssize writeTaskResult = g_task_propagate_int(G_TASK(result), &error.outPtr());
928     if (writeTaskResult != -1)
929         bytesWritten = writeTaskResult;
930 #endif
931     if (error)
932         task->didFailDownload(downloadDestinationError(task->m_response, error->message));
933     else
934         task->didWriteDownload(bytesWritten);
935 }
936
937 void NetworkDataTaskSoup::writeDownload()
938 {
939     RefPtr<NetworkDataTaskSoup> protectedThis(this);
940 #if GLIB_CHECK_VERSION(2, 44, 0)
941     g_output_stream_write_all_async(m_downloadOutputStream.get(), m_readBuffer.data(), m_readBuffer.size(), RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(),
942         reinterpret_cast<GAsyncReadyCallback>(writeDownloadCallback), protectedThis.leakRef());
943 #else
944     GRefPtr<GTask> writeTask = adoptGRef(g_task_new(m_downloadOutputStream.get(), m_cancellable.get(),
945         reinterpret_cast<GAsyncReadyCallback>(writeDownloadCallback), protectedThis.leakRef()));
946     g_task_set_task_data(writeTask.get(), this, nullptr);
947     g_task_run_in_thread(writeTask.get(), [](GTask* writeTask, gpointer source, gpointer userData, GCancellable* cancellable) {
948         auto* task = static_cast<NetworkDataTaskSoup*>(userData);
949         GOutputStream* outputStream = G_OUTPUT_STREAM(source);
950         RELEASE_ASSERT(task->m_downloadOutputStream.get() == outputStream);
951         RELEASE_ASSERT(task->m_cancellable.get() == cancellable);
952         GError* error = nullptr;
953         if (g_cancellable_set_error_if_cancelled(cancellable, &error)) {
954             g_task_return_error(writeTask, error);
955             return;
956         }
957
958         gsize bytesWritten;
959         if (g_output_stream_write_all(outputStream, task->m_readBuffer.data(), task->m_readBuffer.size(), &bytesWritten, cancellable, &error))
960             g_task_return_int(writeTask, bytesWritten);
961         else
962             g_task_return_error(writeTask, error);
963     });
964 #endif
965 }
966
967 void NetworkDataTaskSoup::didWriteDownload(gsize bytesWritten)
968 {
969     ASSERT(bytesWritten == m_readBuffer.size());
970     auto* download = m_session->networkProcess().downloadManager().download(m_pendingDownloadID);
971     ASSERT(download);
972     download->didReceiveData(bytesWritten);
973     read();
974 }
975
976 void NetworkDataTaskSoup::didFinishDownload()
977 {
978     ASSERT(!m_response.isNull());
979     ASSERT(m_downloadOutputStream);
980     g_output_stream_close(m_downloadOutputStream.get(), nullptr, nullptr);
981     m_downloadOutputStream = nullptr;
982
983     ASSERT(m_downloadDestinationFile);
984     ASSERT(m_downloadIntermediateFile);
985     GUniqueOutPtr<GError> error;
986     if (!g_file_move(m_downloadIntermediateFile.get(), m_downloadDestinationFile.get(), G_FILE_COPY_OVERWRITE, m_cancellable.get(), nullptr, nullptr, &error.outPtr())) {
987         didFailDownload(downloadDestinationError(m_response, error->message));
988         return;
989     }
990
991     GRefPtr<GFileInfo> info = adoptGRef(g_file_info_new());
992     CString uri = m_response.url().string().utf8();
993     g_file_info_set_attribute_string(info.get(), "metadata::download-uri", uri.data());
994     g_file_info_set_attribute_string(info.get(), "xattr::xdg.origin.url", uri.data());
995     g_file_set_attributes_async(m_downloadDestinationFile.get(), info.get(), G_FILE_QUERY_INFO_NONE, RunLoopSourcePriority::AsyncIONetwork, nullptr, nullptr, nullptr);
996
997     clearRequest();
998     auto* download = m_session->networkProcess().downloadManager().download(m_pendingDownloadID);
999     ASSERT(download);
1000     download->didFinish();
1001 }
1002
1003 void NetworkDataTaskSoup::didFailDownload(const ResourceError& error)
1004 {
1005     clearRequest();
1006     cleanDownloadFiles();
1007     if (m_client)
1008         dispatchDidCompleteWithError(error);
1009     else {
1010         auto* download = m_session->networkProcess().downloadManager().download(m_pendingDownloadID);
1011         ASSERT(download);
1012         download->didFail(error, IPC::DataReference());
1013     }
1014 }
1015
1016 void NetworkDataTaskSoup::cleanDownloadFiles()
1017 {
1018     if (m_downloadDestinationFile) {
1019         g_file_delete(m_downloadDestinationFile.get(), nullptr, nullptr);
1020         m_downloadDestinationFile = nullptr;
1021     }
1022     if (m_downloadIntermediateFile) {
1023         g_file_delete(m_downloadIntermediateFile.get(), nullptr, nullptr);
1024         m_downloadIntermediateFile = nullptr;
1025     }
1026 }
1027
1028 void NetworkDataTaskSoup::didFail(const ResourceError& error)
1029 {
1030     if (isDownload()) {
1031         didFailDownload(downloadNetworkError(error.failingURL(), error.localizedDescription()));
1032         return;
1033     }
1034
1035     clearRequest();
1036     ASSERT(m_client);
1037     dispatchDidCompleteWithError(error);
1038 }
1039
1040 void NetworkDataTaskSoup::networkEventCallback(SoupMessage* soupMessage, GSocketClientEvent event, GIOStream* stream, NetworkDataTaskSoup* task)
1041 {
1042     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
1043         return;
1044
1045     ASSERT(task->m_soupMessage.get() == soupMessage);
1046     task->networkEvent(event, stream);
1047 }
1048
1049 void NetworkDataTaskSoup::networkEvent(GSocketClientEvent event, GIOStream* stream)
1050 {
1051     Seconds deltaTime = MonotonicTime::now() - m_startTime;
1052     switch (event) {
1053     case G_SOCKET_CLIENT_RESOLVING:
1054         m_networkLoadMetrics.domainLookupStart = deltaTime;
1055         break;
1056     case G_SOCKET_CLIENT_RESOLVED:
1057         m_networkLoadMetrics.domainLookupEnd = deltaTime;
1058         break;
1059     case G_SOCKET_CLIENT_CONNECTING:
1060         m_networkLoadMetrics.connectStart = deltaTime;
1061         break;
1062     case G_SOCKET_CLIENT_CONNECTED:
1063         // Web Timing considers that connection time involves dns, proxy & TLS negotiation...
1064         // so we better pick G_SOCKET_CLIENT_COMPLETE for connectEnd
1065         break;
1066     case G_SOCKET_CLIENT_PROXY_NEGOTIATING:
1067         break;
1068     case G_SOCKET_CLIENT_PROXY_NEGOTIATED:
1069         break;
1070     case G_SOCKET_CLIENT_TLS_HANDSHAKING:
1071         m_networkLoadMetrics.secureConnectionStart = deltaTime;
1072         RELEASE_ASSERT(G_IS_TLS_CONNECTION(stream));
1073         g_object_set_data(G_OBJECT(stream), "wk-soup-message", m_soupMessage.get());
1074         g_signal_connect(stream, "accept-certificate", G_CALLBACK(tlsConnectionAcceptCertificateCallback), this);
1075         break;
1076     case G_SOCKET_CLIENT_TLS_HANDSHAKED:
1077         break;
1078     case G_SOCKET_CLIENT_COMPLETE:
1079         m_networkLoadMetrics.connectEnd = deltaTime;
1080         break;
1081     default:
1082         ASSERT_NOT_REACHED();
1083         break;
1084     }
1085 }
1086
1087 #if SOUP_CHECK_VERSION(2, 49, 91)
1088 void NetworkDataTaskSoup::startingCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
1089 {
1090     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
1091         return;
1092
1093     ASSERT(task->m_soupMessage.get() == soupMessage);
1094     task->didStartRequest();
1095 }
1096 #else
1097 void NetworkDataTaskSoup::requestStartedCallback(SoupSession* session, SoupMessage* soupMessage, SoupSocket*, NetworkDataTaskSoup* task)
1098 {
1099     ASSERT(session == static_cast<NetworkSessionSoup&>(task->m_session.get()).soupSession());
1100     if (soupMessage != task->m_soupMessage.get())
1101         return;
1102
1103     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
1104         return;
1105
1106     task->didStartRequest();
1107 }
1108 #endif
1109
1110 void NetworkDataTaskSoup::didStartRequest()
1111 {
1112     m_networkLoadMetrics.requestStart = MonotonicTime::now() - m_startTime;
1113 }
1114
1115 void NetworkDataTaskSoup::restartedCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
1116 {
1117     // Called each time the message is going to be sent again except the first time.
1118     // This happens when libsoup handles HTTP authentication.
1119     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
1120         return;
1121
1122     ASSERT(task->m_soupMessage.get() == soupMessage);
1123     task->didRestart();
1124 }
1125
1126 void NetworkDataTaskSoup::didRestart()
1127 {
1128     m_startTime = MonotonicTime::now();
1129     m_networkLoadMetrics.reset();
1130 }
1131
1132 } // namespace WebKit
1133