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