b253dc5ac55ab56899c5db93867c5d01c3f20b7b
[WebKit-https.git] / Source / WebCore / platform / network / soup / ResourceHandleSoup.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2008 Xan Lopez <xan@gnome.org>
5  * Copyright (C) 2008, 2010 Collabora Ltd.
6  * Copyright (C) 2009 Holger Hans Peter Freyther
7  * Copyright (C) 2009, 2013 Gustavo Noronha Silva <gns@gnome.org>
8  * Copyright (C) 2009 Christian Dywan <christian@imendio.com>
9  * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
10  * Copyright (C) 2009 John Kjellberg <john.kjellberg@power.alstom.com>
11  * Copyright (C) 2012 Intel Corporation
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Library General Public License for more details.
22  *
23  * You should have received a copy of the GNU Library General Public License
24  * along with this library; see the file COPYING.LIB.  If not, write to
25  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26  * Boston, MA 02110-1301, USA.
27  */
28
29 #include "config.h"
30 #include "ResourceHandle.h"
31
32 #if USE(SOUP)
33
34 #include "CredentialStorage.h"
35 #include "FileSystem.h"
36 #include "GUniquePtrSoup.h"
37 #include "HTTPParsers.h"
38 #include "LocalizedStrings.h"
39 #include "MIMETypeRegistry.h"
40 #include "NetworkStorageSession.h"
41 #include "NetworkingContext.h"
42 #include "ResourceError.h"
43 #include "ResourceHandleClient.h"
44 #include "ResourceHandleInternal.h"
45 #include "ResourceResponse.h"
46 #include "SharedBuffer.h"
47 #include "SoupNetworkSession.h"
48 #include "TextEncoding.h"
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <gio/gio.h>
52 #include <glib.h>
53 #include <libsoup/soup.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 #if !COMPILER(MSVC)
57 #include <unistd.h>
58 #endif
59 #include <wtf/CompletionHandler.h>
60 #include <wtf/CurrentTime.h>
61 #include <wtf/glib/GRefPtr.h>
62 #include <wtf/glib/RunLoopSourcePriority.h>
63 #include <wtf/text/CString.h>
64
65 namespace WebCore {
66
67 static const size_t gDefaultReadBufferSize = 8192;
68
69 static bool createSoupRequestAndMessageForHandle(ResourceHandle*, const ResourceRequest&);
70 static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying = false);
71 static void sendRequestCallback(GObject*, GAsyncResult*, gpointer);
72 static void readCallback(GObject*, GAsyncResult*, gpointer);
73 static void continueAfterDidReceiveResponse(ResourceHandle*);
74
75 ResourceHandleInternal::~ResourceHandleInternal() = default;
76
77 static SoupSession* sessionFromContext(NetworkingContext* context)
78 {
79     if (!context || !context->isValid())
80         return NetworkStorageSession::defaultStorageSession().getOrCreateSoupNetworkSession().soupSession();
81     return context->storageSession().getOrCreateSoupNetworkSession().soupSession();
82 }
83
84 ResourceHandle::~ResourceHandle()
85 {
86     cleanupSoupRequestOperation(this, true);
87 }
88
89 SoupSession* ResourceHandleInternal::soupSession()
90 {
91     return m_session ? m_session->soupSession() : sessionFromContext(m_context.get());
92 }
93
94 RefPtr<ResourceHandle> ResourceHandle::create(SoupNetworkSession& session, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff)
95 {
96     auto newHandle = adoptRef(*new ResourceHandle(session, request, client, defersLoading, shouldContentSniff, shouldContentEncodingSniff));
97
98     if (newHandle->d->m_scheduledFailureType != NoFailure)
99         return WTFMove(newHandle);
100
101     if (newHandle->start())
102         return WTFMove(newHandle);
103
104     return nullptr;
105 }
106
107 ResourceHandle::ResourceHandle(SoupNetworkSession& session, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff)
108     : d(std::make_unique<ResourceHandleInternal>(this, nullptr, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()), shouldContentEncodingSniff))
109 {
110     if (!request.url().isValid()) {
111         scheduleFailure(InvalidURLFailure);
112         return;
113     }
114
115     if (!portAllowed(request.url())) {
116         scheduleFailure(BlockedFailure);
117         return;
118     }
119
120     d->m_session = &session;
121 }
122
123 bool ResourceHandle::cancelledOrClientless()
124 {
125     if (!client())
126         return true;
127
128     return getInternal()->m_cancelled;
129 }
130
131 void ResourceHandle::ensureReadBuffer()
132 {
133     ResourceHandleInternal* d = getInternal();
134
135     if (d->m_soupBuffer)
136         return;
137
138
139     auto* buffer = static_cast<uint8_t*>(fastMalloc(gDefaultReadBufferSize));
140     d->m_soupBuffer.reset(soup_buffer_new_with_owner(buffer, gDefaultReadBufferSize, buffer, fastFree));
141
142     ASSERT(d->m_soupBuffer);
143 }
144
145 static bool isAuthenticationFailureStatusCode(int httpStatusCode)
146 {
147     return httpStatusCode == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || httpStatusCode == SOUP_STATUS_UNAUTHORIZED;
148 }
149
150 static void tlsErrorsChangedCallback(SoupMessage* message, GParamSpec*, gpointer data)
151 {
152     RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
153     if (!handle || handle->cancelledOrClientless())
154         return;
155
156     SoupNetworkSession::checkTLSErrors(handle->getInternal()->m_soupRequest.get(), message, [handle] (const ResourceError& error) {
157         if (error.isNull())
158             return;
159
160         handle->client()->didFail(handle.get(), error);
161         handle->cancel();
162     });
163 }
164
165 static void gotHeadersCallback(SoupMessage* message, gpointer data)
166 {
167     ResourceHandle* handle = static_cast<ResourceHandle*>(data);
168     if (!handle || handle->cancelledOrClientless())
169         return;
170
171     ResourceHandleInternal* d = handle->getInternal();
172
173     if (d->m_context && d->m_context->isValid()) {
174         // We are a bit more conservative with the persistent credential storage than the session store,
175         // since we are waiting until we know that this authentication succeeded before actually storing.
176         // This is because we want to avoid hitting the disk twice (once to add and once to remove) for
177         // incorrect credentials or polluting the keychain with invalid credentials.
178         if (!isAuthenticationFailureStatusCode(message->status_code) && message->status_code < 500) {
179             d->m_context->storageSession().saveCredentialToPersistentStorage(
180                 d->m_credentialDataToSaveInPersistentStore.protectionSpace,
181                 d->m_credentialDataToSaveInPersistentStore.credential);
182         }
183     }
184
185     // The original response will be needed later to feed to willSendRequest in
186     // doRedirect() in case we are redirected. For this reason, we store it here.
187     d->m_response.updateFromSoupMessage(message);
188 }
189
190 static void applyAuthenticationToRequest(ResourceHandle* handle, ResourceRequest& request, bool redirect)
191 {
192     // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
193     ResourceHandleInternal* d = handle->getInternal();
194
195     String partition = request.cachePartition();
196
197     if (handle->shouldUseCredentialStorage()) {
198         if (d->m_user.isEmpty() && d->m_pass.isEmpty())
199             d->m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.url());
200         else if (!redirect) {
201             // If there is already a protection space known for the URL, update stored credentials
202             // before sending a request. This makes it possible to implement logout by sending an
203             // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
204             // an authentication dialog doesn't pop up).
205             CredentialStorage::defaultCredentialStorage().set(partition, Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
206         }
207     }
208
209     String user = d->m_user;
210     String password = d->m_pass;
211     if (!d->m_initialCredential.isEmpty()) {
212         user = d->m_initialCredential.user();
213         password = d->m_initialCredential.password();
214     }
215
216     if (user.isEmpty() && password.isEmpty()) {
217         // In case credential is not available from the handle and credential storage should not to be used,
218         // disable authentication manager so that credentials stored in libsoup are not used.
219         d->m_useAuthenticationManager = handle->shouldUseCredentialStorage();
220         return;
221     }
222
223     // We always put the credentials into the URL. In the CFNetwork-port HTTP family credentials are applied in
224     // the didReceiveAuthenticationChallenge callback, but libsoup requires us to use this method to override
225     // any previously remembered credentials. It has its own per-session credential storage.
226     URL urlWithCredentials(request.url());
227     urlWithCredentials.setUser(user);
228     urlWithCredentials.setPass(password);
229     request.setURL(urlWithCredentials);
230 }
231
232 // Called each time the message is going to be sent again except the first time.
233 // This happens when libsoup handles HTTP authentication.
234 static void restartedCallback(SoupMessage*, gpointer data)
235 {
236     ResourceHandle* handle = static_cast<ResourceHandle*>(data);
237     if (!handle || handle->cancelledOrClientless())
238         return;
239
240     handle->m_requestTime = MonotonicTime::now();
241 }
242
243 static bool shouldRedirect(ResourceHandle* handle)
244 {
245     ResourceHandleInternal* d = handle->getInternal();
246     SoupMessage* message = d->m_soupMessage.get();
247
248     // Some 3xx status codes aren't actually redirects.
249     if (message->status_code == 300 || message->status_code == 304 || message->status_code == 305 || message->status_code == 306)
250         return false;
251
252     if (!soup_message_headers_get_one(message->response_headers, "Location"))
253         return false;
254
255     return true;
256 }
257
258 static bool shouldRedirectAsGET(SoupMessage* message, URL& newURL, bool crossOrigin)
259 {
260     if (message->method == SOUP_METHOD_GET || message->method == SOUP_METHOD_HEAD)
261         return false;
262
263     if (!newURL.protocolIsInHTTPFamily())
264         return true;
265
266     switch (message->status_code) {
267     case SOUP_STATUS_SEE_OTHER:
268         return true;
269     case SOUP_STATUS_FOUND:
270     case SOUP_STATUS_MOVED_PERMANENTLY:
271         if (message->method == SOUP_METHOD_POST)
272             return true;
273         break;
274     }
275
276     if (crossOrigin && message->method == SOUP_METHOD_DELETE)
277         return true;
278
279     return false;
280 }
281
282 static void continueAfterWillSendRequest(ResourceHandle* handle, ResourceRequest&& request)
283 {
284     // willSendRequest might cancel the load.
285     if (handle->cancelledOrClientless())
286         return;
287
288     ResourceHandleInternal* d = handle->getInternal();
289     if (protocolHostAndPortAreEqual(request.url(), d->m_response.url()))
290         applyAuthenticationToRequest(handle, request, true);
291
292     if (!createSoupRequestAndMessageForHandle(handle, request)) {
293         d->client()->cannotShowURL(handle);
294         return;
295     }
296
297     handle->sendPendingRequest();
298 }
299
300 static void doRedirect(ResourceHandle* handle)
301 {
302     ResourceHandleInternal* d = handle->getInternal();
303     static const int maxRedirects = 20;
304
305     if (d->m_redirectCount++ > maxRedirects) {
306         d->client()->didFail(handle, ResourceError::transportError(d->m_soupRequest.get(), SOUP_STATUS_TOO_MANY_REDIRECTS, "Too many redirects"));
307         cleanupSoupRequestOperation(handle);
308         return;
309     }
310
311     ResourceRequest newRequest = handle->firstRequest();
312     SoupMessage* message = d->m_soupMessage.get();
313     const char* location = soup_message_headers_get_one(message->response_headers, "Location");
314     URL newURL = URL(URL(soup_message_get_uri(message)), location);
315     bool crossOrigin = !protocolHostAndPortAreEqual(handle->firstRequest().url(), newURL);
316     newRequest.setURL(newURL);
317
318     if (newRequest.httpMethod() != "GET") {
319         // Change newRequest method to GET if change was made during a previous redirection
320         // or if current redirection says so
321         if (message->method == SOUP_METHOD_GET || shouldRedirectAsGET(message, newURL, crossOrigin)) {
322             newRequest.setHTTPMethod("GET");
323             newRequest.setHTTPBody(nullptr);
324             newRequest.clearHTTPContentType();
325         }
326     }
327
328     // Should not set Referer after a redirect from a secure resource to non-secure one.
329     if (!newURL.protocolIs("https") && protocolIs(newRequest.httpReferrer(), "https") && handle->context()->shouldClearReferrerOnHTTPSToHTTPRedirect())
330         newRequest.clearHTTPReferrer();
331
332     d->m_user = newURL.user();
333     d->m_pass = newURL.pass();
334     newRequest.removeCredentials();
335
336     if (crossOrigin) {
337         // If the network layer carries over authentication headers from the original request
338         // in a cross-origin redirect, we want to clear those headers here. 
339         newRequest.clearHTTPAuthorization();
340         newRequest.clearHTTPOrigin();
341
342         // TODO: We are losing any username and password specified in the redirect URL, as this is the 
343         // same behavior as the CFNet port. We should investigate if this is really what we want.
344     }
345
346     cleanupSoupRequestOperation(handle);
347
348     ResourceResponse responseCopy = d->m_response;
349     d->client()->willSendRequestAsync(handle, WTFMove(newRequest), WTFMove(responseCopy), [handle = makeRef(*handle)] (ResourceRequest&& request) {
350         continueAfterWillSendRequest(handle.ptr(), WTFMove(request));
351     });
352 }
353
354 static void redirectSkipCallback(GObject*, GAsyncResult* asyncResult, gpointer data)
355 {
356     RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
357
358     if (handle->cancelledOrClientless()) {
359         cleanupSoupRequestOperation(handle.get());
360         return;
361     }
362
363     GUniqueOutPtr<GError> error;
364     ResourceHandleInternal* d = handle->getInternal();
365     gssize bytesSkipped = g_input_stream_skip_finish(d->m_inputStream.get(), asyncResult, &error.outPtr());
366     if (error) {
367         handle->client()->didFail(handle.get(), ResourceError::genericGError(error.get(), d->m_soupRequest.get()));
368         cleanupSoupRequestOperation(handle.get());
369         return;
370     }
371
372     if (bytesSkipped > 0) {
373         g_input_stream_skip_async(d->m_inputStream.get(), gDefaultReadBufferSize, RunLoopSourcePriority::AsyncIONetwork,
374             d->m_cancellable.get(), redirectSkipCallback, handle.get());
375         return;
376     }
377
378     g_input_stream_close(d->m_inputStream.get(), 0, 0);
379     doRedirect(handle.get());
380 }
381
382 static void wroteBodyDataCallback(SoupMessage*, SoupBuffer* buffer, gpointer data)
383 {
384     RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
385     if (!handle)
386         return;
387
388     ASSERT(buffer);
389     ResourceHandleInternal* d = handle->getInternal();
390     d->m_bodyDataSent += buffer->length;
391
392     if (handle->cancelledOrClientless())
393         return;
394
395     handle->client()->didSendData(handle.get(), d->m_bodyDataSent, d->m_bodySize);
396 }
397
398 static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroying)
399 {
400     ResourceHandleInternal* d = handle->getInternal();
401
402     d->m_soupRequest.clear();
403     d->m_inputStream.clear();
404     d->m_multipartInputStream.clear();
405     d->m_cancellable.clear();
406     d->m_soupBuffer.reset();
407
408     if (d->m_soupMessage) {
409         g_signal_handlers_disconnect_matched(d->m_soupMessage.get(), G_SIGNAL_MATCH_DATA,
410                                              0, 0, 0, 0, handle);
411         g_object_set_data(G_OBJECT(d->m_soupMessage.get()), "handle", 0);
412         d->m_soupMessage.clear();
413     }
414
415     d->m_timeoutSource.stop();
416
417     if (!isDestroying)
418         handle->deref();
419 }
420
421 size_t ResourceHandle::currentStreamPosition() const
422 {
423     GInputStream* baseStream = d->m_inputStream.get();
424     while (!G_IS_SEEKABLE(baseStream) && G_IS_FILTER_INPUT_STREAM(baseStream))
425         baseStream = g_filter_input_stream_get_base_stream(G_FILTER_INPUT_STREAM(baseStream));
426
427     if (!G_IS_SEEKABLE(baseStream))
428         return 0;
429
430     return g_seekable_tell(G_SEEKABLE(baseStream));
431 }
432
433 static void nextMultipartResponsePartCallback(GObject* /*source*/, GAsyncResult* result, gpointer data)
434 {
435     RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
436
437     if (handle->cancelledOrClientless()) {
438         cleanupSoupRequestOperation(handle.get());
439         return;
440     }
441
442     ResourceHandleInternal* d = handle->getInternal();
443     ASSERT(!d->m_inputStream);
444
445     GUniqueOutPtr<GError> error;
446     d->m_inputStream = adoptGRef(soup_multipart_input_stream_next_part_finish(d->m_multipartInputStream.get(), result, &error.outPtr()));
447
448     if (error) {
449         handle->client()->didFail(handle.get(), ResourceError::httpError(d->m_soupMessage.get(), error.get(), d->m_soupRequest.get()));
450         cleanupSoupRequestOperation(handle.get());
451         return;
452     }
453
454     if (!d->m_inputStream) {
455         handle->client()->didFinishLoading(handle.get());
456         cleanupSoupRequestOperation(handle.get());
457         return;
458     }
459
460     d->m_response = ResourceResponse();
461     d->m_response.setURL(handle->firstRequest().url());
462     d->m_response.updateFromSoupMessageHeaders(soup_multipart_input_stream_get_headers(d->m_multipartInputStream.get()));
463
464     d->m_previousPosition = 0;
465
466     handle->didReceiveResponse(ResourceResponse(d->m_response));
467 }
468
469 static void sendRequestCallback(GObject*, GAsyncResult* result, gpointer data)
470 {
471     RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
472
473     if (handle->cancelledOrClientless()) {
474         cleanupSoupRequestOperation(handle.get());
475         return;
476     }
477
478     ResourceHandleInternal* d = handle->getInternal();
479     SoupMessage* soupMessage = d->m_soupMessage.get();
480
481
482     if (d->m_defersLoading) {
483         d->m_deferredResult = result;
484         return;
485     }
486
487     GUniqueOutPtr<GError> error;
488     GRefPtr<GInputStream> inputStream = adoptGRef(soup_request_send_finish(d->m_soupRequest.get(), result, &error.outPtr()));
489     if (error) {
490         handle->client()->didFail(handle.get(), ResourceError::httpError(soupMessage, error.get(), d->m_soupRequest.get()));
491         cleanupSoupRequestOperation(handle.get());
492         return;
493     }
494
495     if (soupMessage) {
496         if (handle->shouldContentSniff() && soupMessage->status_code != SOUP_STATUS_NOT_MODIFIED) {
497             const char* sniffedType = soup_request_get_content_type(d->m_soupRequest.get());
498             d->m_response.setSniffedContentType(sniffedType);
499         }
500         d->m_response.updateFromSoupMessage(soupMessage);
501
502         if (SOUP_STATUS_IS_REDIRECTION(soupMessage->status_code) && shouldRedirect(handle.get())) {
503             d->m_inputStream = inputStream;
504             g_input_stream_skip_async(d->m_inputStream.get(), gDefaultReadBufferSize, RunLoopSourcePriority::AsyncIONetwork,
505                 d->m_cancellable.get(), redirectSkipCallback, handle.get());
506             return;
507         }
508     } else {
509         d->m_response.setURL(handle->firstRequest().url());
510         const gchar* contentType = soup_request_get_content_type(d->m_soupRequest.get());
511         d->m_response.setMimeType(extractMIMETypeFromMediaType(contentType));
512         d->m_response.setTextEncodingName(extractCharsetFromMediaType(contentType));
513         d->m_response.setExpectedContentLength(soup_request_get_content_length(d->m_soupRequest.get()));
514     }
515
516     d->m_response.deprecatedNetworkLoadMetrics().responseStart = MonotonicTime::now() - handle->m_requestTime;
517
518     if (soupMessage && d->m_response.isMultipart())
519         d->m_multipartInputStream = adoptGRef(soup_multipart_input_stream_new(soupMessage, inputStream.get()));
520     else
521         d->m_inputStream = inputStream;
522
523     handle->didReceiveResponse(ResourceResponse(d->m_response));
524 }
525
526 void ResourceHandle::platformContinueSynchronousDidReceiveResponse()
527 {
528     continueAfterDidReceiveResponse(this);
529 }
530
531 static void continueAfterDidReceiveResponse(ResourceHandle* handle)
532 {
533     if (handle->cancelledOrClientless()) {
534         cleanupSoupRequestOperation(handle);
535         return;
536     }
537
538     ResourceHandleInternal* d = handle->getInternal();
539     if (d->m_soupMessage && d->m_multipartInputStream && !d->m_inputStream) {
540         soup_multipart_input_stream_next_part_async(d->m_multipartInputStream.get(), RunLoopSourcePriority::AsyncIONetwork,
541             d->m_cancellable.get(), nextMultipartResponsePartCallback, handle);
542         return;
543     }
544
545     ASSERT(d->m_inputStream);
546     handle->ensureReadBuffer();
547     g_input_stream_read_async(d->m_inputStream.get(), const_cast<char*>(d->m_soupBuffer->data), d->m_soupBuffer->length,
548         RunLoopSourcePriority::AsyncIONetwork, d->m_cancellable.get(), readCallback, handle);
549 }
550
551 void ResourceHandle::didStartRequest()
552 {
553     getInternal()->m_response.deprecatedNetworkLoadMetrics().requestStart = MonotonicTime::now() - m_requestTime;
554 }
555
556 #if SOUP_CHECK_VERSION(2, 49, 91)
557 static void startingCallback(SoupMessage*, ResourceHandle* handle)
558 {
559     handle->didStartRequest();
560 }
561 #endif // SOUP_CHECK_VERSION(2, 49, 91)
562
563 static void networkEventCallback(SoupMessage*, GSocketClientEvent event, GIOStream*, gpointer data)
564 {
565     ResourceHandle* handle = static_cast<ResourceHandle*>(data);
566     if (!handle)
567         return;
568
569     if (handle->cancelledOrClientless())
570         return;
571
572     ResourceHandleInternal* d = handle->getInternal();
573     Seconds deltaTime = MonotonicTime::now() - handle->m_requestTime;
574     switch (event) {
575     case G_SOCKET_CLIENT_RESOLVING:
576         d->m_response.deprecatedNetworkLoadMetrics().domainLookupStart = deltaTime;
577         break;
578     case G_SOCKET_CLIENT_RESOLVED:
579         d->m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = deltaTime;
580         break;
581     case G_SOCKET_CLIENT_CONNECTING:
582         d->m_response.deprecatedNetworkLoadMetrics().connectStart = deltaTime;
583         if (d->m_response.deprecatedNetworkLoadMetrics().domainLookupStart != Seconds(-1)) {
584             // WebCore/inspector/front-end/RequestTimingView.js assumes
585             // that DNS time is included in connection time so must
586             // substract here the DNS delta that will be added later (see
587             // WebInspector.RequestTimingView.createTimingTable in the
588             // file above for more details).
589             d->m_response.deprecatedNetworkLoadMetrics().connectStart -=
590                 d->m_response.deprecatedNetworkLoadMetrics().domainLookupEnd - d->m_response.deprecatedNetworkLoadMetrics().domainLookupStart;
591         }
592         break;
593     case G_SOCKET_CLIENT_CONNECTED:
594         // Web Timing considers that connection time involves dns, proxy & TLS negotiation...
595         // so we better pick G_SOCKET_CLIENT_COMPLETE for connectEnd
596         break;
597     case G_SOCKET_CLIENT_PROXY_NEGOTIATING:
598         break;
599     case G_SOCKET_CLIENT_PROXY_NEGOTIATED:
600         break;
601     case G_SOCKET_CLIENT_TLS_HANDSHAKING:
602         d->m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = deltaTime;
603         break;
604     case G_SOCKET_CLIENT_TLS_HANDSHAKED:
605         break;
606     case G_SOCKET_CLIENT_COMPLETE:
607         d->m_response.deprecatedNetworkLoadMetrics().connectEnd = deltaTime;
608         break;
609     default:
610         ASSERT_NOT_REACHED();
611         break;
612     }
613 }
614
615 static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const ResourceRequest& request)
616 {
617     ASSERT(handle);
618
619     ResourceHandleInternal* d = handle->getInternal();
620     ASSERT(d->m_soupRequest);
621
622     d->m_soupMessage = adoptGRef(soup_request_http_get_message(SOUP_REQUEST_HTTP(d->m_soupRequest.get())));
623     if (!d->m_soupMessage)
624         return false;
625
626     SoupMessage* soupMessage = d->m_soupMessage.get();
627     request.updateSoupMessage(soupMessage);
628     d->m_bodySize = soupMessage->request_body->length;
629
630     g_object_set_data(G_OBJECT(soupMessage), "handle", handle);
631     if (!handle->shouldContentSniff())
632         soup_message_disable_feature(soupMessage, SOUP_TYPE_CONTENT_SNIFFER);
633     if (!d->m_useAuthenticationManager)
634         soup_message_disable_feature(soupMessage, SOUP_TYPE_AUTH_MANAGER);
635
636     // Make sure we have an Accept header for subresources; some sites
637     // want this to serve some of their subresources
638     if (!soup_message_headers_get_one(soupMessage->request_headers, "Accept"))
639         soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*");
640
641     // In the case of XHR .send() and .send("") explicitly tell libsoup to send a zero content-lenght header
642     // for consistency with other backends (e.g. Chromium's) and other UA implementations like FF. It's done
643     // in the backend here instead of in XHR code since in XHR CORS checking prevents us from this kind of
644     // late header manipulation.
645     if ((request.httpMethod() == "POST" || request.httpMethod() == "PUT") && !d->m_bodySize)
646         soup_message_headers_set_content_length(soupMessage->request_headers, 0);
647
648     g_signal_connect(d->m_soupMessage.get(), "notify::tls-errors", G_CALLBACK(tlsErrorsChangedCallback), handle);
649     g_signal_connect(d->m_soupMessage.get(), "got-headers", G_CALLBACK(gotHeadersCallback), handle);
650     g_signal_connect(d->m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle);
651
652     unsigned flags = SOUP_MESSAGE_NO_REDIRECT;
653     soup_message_set_flags(d->m_soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(d->m_soupMessage.get()) | flags));
654
655 #if SOUP_CHECK_VERSION(2, 49, 91)
656     g_signal_connect(d->m_soupMessage.get(), "starting", G_CALLBACK(startingCallback), handle);
657 #endif
658     g_signal_connect(d->m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), handle);
659     g_signal_connect(d->m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), handle);
660
661 #if SOUP_CHECK_VERSION(2, 43, 1)
662     soup_message_set_priority(d->m_soupMessage.get(), toSoupMessagePriority(request.priority()));
663 #endif
664
665     return true;
666 }
667
668 static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const ResourceRequest& request)
669 {
670     ResourceHandleInternal* d = handle->getInternal();
671
672     GUniquePtr<SoupURI> soupURI = request.createSoupURI();
673     if (!soupURI)
674         return false;
675
676     GUniqueOutPtr<GError> error;
677     d->m_soupRequest = adoptGRef(soup_session_request_uri(d->soupSession(), soupURI.get(), &error.outPtr()));
678     if (error) {
679         d->m_soupRequest.clear();
680         return false;
681     }
682
683     // SoupMessages are only applicable to HTTP-family requests.
684     if (request.url().protocolIsInHTTPFamily() && !createSoupMessageForHandleAndRequest(handle, request)) {
685         d->m_soupRequest.clear();
686         return false;
687     }
688
689     request.updateSoupRequest(d->m_soupRequest.get());
690
691     return true;
692 }
693
694 bool ResourceHandle::start()
695 {
696     ASSERT(!d->m_soupMessage);
697
698     // The frame could be null if the ResourceHandle is not associated to any
699     // Frame, e.g. if we are downloading a file.
700     // If the frame is not null but the page is null this must be an attempted
701     // load from an unload handler, so let's just block it.
702     // If both the frame and the page are not null the context is valid.
703     if (d->m_context && !d->m_context->isValid())
704         return false;
705
706     // Only allow the POST and GET methods for non-HTTP requests.
707     const ResourceRequest& request = firstRequest();
708     if (!request.url().protocolIsInHTTPFamily() && request.httpMethod() != "GET" && request.httpMethod() != "POST") {
709         this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
710         return true;
711     }
712
713     applyAuthenticationToRequest(this, firstRequest(), false);
714
715     if (!createSoupRequestAndMessageForHandle(this, request)) {
716         this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
717         return true;
718     }
719
720     // Send the request only if it's not been explicitly deferred.
721     if (!d->m_defersLoading)
722         sendPendingRequest();
723
724     return true;
725 }
726
727 RefPtr<ResourceHandle> ResourceHandle::releaseForDownload(ResourceHandleClient* downloadClient)
728 {
729     // We don't adopt the ref, as it will be released by cleanupSoupRequestOperation, which should always run.
730     ResourceHandle* newHandle = new ResourceHandle(d->m_context.get(), firstRequest(), nullptr, d->m_defersLoading, d->m_shouldContentSniff, d->m_shouldContentEncodingSniff);
731     newHandle->relaxAdoptionRequirement();
732     std::swap(d, newHandle->d);
733
734     g_signal_handlers_disconnect_matched(newHandle->d->m_soupMessage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
735     g_object_set_data(G_OBJECT(newHandle->d->m_soupMessage.get()), "handle", newHandle);
736
737     newHandle->d->m_client = downloadClient;
738     continueAfterDidReceiveResponse(newHandle);
739
740     return newHandle;
741 }
742
743 void ResourceHandle::timeoutFired()
744 {
745     client()->didFail(this, ResourceError::timeoutError(firstRequest().url()));
746     cancel();
747 }
748
749 void ResourceHandle::sendPendingRequest()
750 {
751     m_requestTime = MonotonicTime::now();
752
753     if (d->m_firstRequest.timeoutInterval() > 0)
754         d->m_timeoutSource.startOneShot(1_s * d->m_firstRequest.timeoutInterval());
755
756     // Balanced by a deref() in cleanupSoupRequestOperation, which should always run.
757     ref();
758
759     d->m_cancellable = adoptGRef(g_cancellable_new());
760     soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, this);
761 }
762
763 void ResourceHandle::cancel()
764 {
765     d->m_cancelled = true;
766     if (d->m_soupMessage)
767         soup_session_cancel_message(d->soupSession(), d->m_soupMessage.get(), SOUP_STATUS_CANCELLED);
768     else if (d->m_cancellable)
769         g_cancellable_cancel(d->m_cancellable.get());
770 }
771
772 bool ResourceHandle::shouldUseCredentialStorage()
773 {
774     return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
775 }
776
777 void ResourceHandle::continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage)
778 {
779     ASSERT(!d->m_currentWebChallenge.isNull());
780     AuthenticationChallenge& challenge = d->m_currentWebChallenge;
781
782     ASSERT(d->m_soupMessage);
783     if (!credentialFromPersistentStorage.isEmpty())
784         challenge.setProposedCredential(credentialFromPersistentStorage);
785
786     if (!client()) {
787         soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get());
788         clearAuthentication();
789         return;
790     }
791
792     client()->didReceiveAuthenticationChallenge(this, challenge);
793 }
794
795 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
796 {
797     ASSERT(d->m_currentWebChallenge.isNull());
798
799     String partition = firstRequest().cachePartition();
800
801     // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
802     bool useCredentialStorage = shouldUseCredentialStorage();
803     if (useCredentialStorage) {
804         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
805             // The stored credential wasn't accepted, stop using it. There is a race condition
806             // here, since a different credential might have already been stored by another
807             // ResourceHandle, but the observable effect should be very minor, if any.
808             CredentialStorage::defaultCredentialStorage().remove(partition, challenge.protectionSpace());
809         }
810
811         if (!challenge.previousFailureCount()) {
812             Credential credential = CredentialStorage::defaultCredentialStorage().get(partition, challenge.protectionSpace());
813             if (!credential.isEmpty() && credential != d->m_initialCredential) {
814                 ASSERT(credential.persistence() == CredentialPersistenceNone);
815
816                 // Store the credential back, possibly adding it as a default for this directory.
817                 if (isAuthenticationFailureStatusCode(challenge.failureResponse().httpStatusCode()))
818                     CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
819
820                 soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
821                 return;
822             }
823         }
824     }
825
826     d->m_currentWebChallenge = challenge;
827     soup_session_pause_message(d->soupSession(), d->m_soupMessage.get());
828
829     // We could also do this before we even start the request, but that would be at the expense
830     // of all request latency, versus a one-time latency for the small subset of requests that
831     // use HTTP authentication. In the end, this doesn't matter much, because persistent credentials
832     // will become session credentials after the first use.
833     if (useCredentialStorage && d->m_context && d->m_context->isValid()) {
834         d->m_context->storageSession().getCredentialFromPersistentStorage(challenge.protectionSpace(), [this, protectedThis = makeRef(*this)] (Credential&& credential) {
835             continueDidReceiveAuthenticationChallenge(WTFMove(credential));
836         });
837         return;
838     }
839
840     continueDidReceiveAuthenticationChallenge(Credential());
841 }
842
843 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
844 {
845     ASSERT(!challenge.isNull());
846     if (challenge != d->m_currentWebChallenge)
847         return;
848     soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get());
849
850     clearAuthentication();
851 }
852
853 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
854 {
855     ASSERT(!challenge.isNull());
856     if (challenge != d->m_currentWebChallenge)
857         return;
858
859     // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
860     if (credential.isEmpty()) {
861         receivedRequestToContinueWithoutCredential(challenge);
862         return;
863     }
864
865     String partition = firstRequest().cachePartition();
866
867     if (shouldUseCredentialStorage()) {
868         // Eventually we will manage per-session credentials only internally or use some newly-exposed API from libsoup,
869         // because once we authenticate via libsoup, there is no way to ignore it for a particular request. Right now,
870         // we place the credentials in the store even though libsoup will never fire the authenticate signal again for
871         // this protection space.
872         if (credential.persistence() == CredentialPersistenceForSession || credential.persistence() == CredentialPersistencePermanent)
873             CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
874
875         if (credential.persistence() == CredentialPersistencePermanent) {
876             d->m_credentialDataToSaveInPersistentStore.credential = credential;
877             d->m_credentialDataToSaveInPersistentStore.protectionSpace = challenge.protectionSpace();
878         }
879     }
880
881     ASSERT(d->m_soupMessage);
882     soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
883     soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get());
884
885     clearAuthentication();
886 }
887
888 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
889 {
890     ASSERT(!challenge.isNull());
891     if (challenge != d->m_currentWebChallenge)
892         return;
893
894     if (cancelledOrClientless()) {
895         clearAuthentication();
896         return;
897     }
898
899     ASSERT(d->m_soupMessage);
900     soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get());
901
902     if (client())
903         client()->receivedCancellation(this, challenge);
904
905     clearAuthentication();
906 }
907
908 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&)
909 {
910     ASSERT_NOT_REACHED();
911 }
912
913 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge& challenge)
914 {
915     // This is only used by layout tests, soup based ports don't implement this.
916     notImplemented();
917     receivedRequestToContinueWithoutCredential(challenge);
918 }
919
920 static bool waitingToSendRequest(ResourceHandle* handle)
921 {
922     // We need to check for d->m_soupRequest because the request may have raised a failure
923     // (for example invalid URLs). We cannot  simply check for d->m_scheduledFailure because
924     // it's cleared as soon as the failure event is fired.
925     return handle->getInternal()->m_soupRequest && !handle->getInternal()->m_cancellable;
926 }
927
928 void ResourceHandle::platformSetDefersLoading(bool defersLoading)
929 {
930     if (cancelledOrClientless())
931         return;
932
933     // Except when canceling a possible timeout timer, we only need to take action here to UN-defer loading.
934     if (defersLoading) {
935         d->m_timeoutSource.stop();
936         return;
937     }
938
939     if (waitingToSendRequest(this)) {
940         sendPendingRequest();
941         return;
942     }
943
944     if (d->m_deferredResult) {
945         GRefPtr<GAsyncResult> asyncResult = adoptGRef(d->m_deferredResult.leakRef());
946
947         if (d->m_inputStream)
948             readCallback(G_OBJECT(d->m_inputStream.get()), asyncResult.get(), this);
949         else
950             sendRequestCallback(G_OBJECT(d->m_soupRequest.get()), asyncResult.get(), this);
951     }
952 }
953
954 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentialsPolicy, ResourceError&, ResourceResponse&, Vector<char>&)
955 {
956     ASSERT_NOT_REACHED();
957 }
958
959 static void readCallback(GObject*, GAsyncResult* asyncResult, gpointer data)
960 {
961     RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
962
963     if (handle->cancelledOrClientless()) {
964         cleanupSoupRequestOperation(handle.get());
965         return;
966     }
967
968     ResourceHandleInternal* d = handle->getInternal();
969     if (d->m_defersLoading) {
970         d->m_deferredResult = asyncResult;
971         return;
972     }
973
974     GUniqueOutPtr<GError> error;
975     gssize bytesRead = g_input_stream_read_finish(d->m_inputStream.get(), asyncResult, &error.outPtr());
976
977     if (error) {
978         handle->client()->didFail(handle.get(), ResourceError::genericGError(error.get(), d->m_soupRequest.get()));
979         cleanupSoupRequestOperation(handle.get());
980         return;
981     }
982
983     if (!bytesRead) {
984         // If this is a multipart message, we'll look for another part.
985         if (d->m_soupMessage && d->m_multipartInputStream) {
986             d->m_inputStream.clear();
987             soup_multipart_input_stream_next_part_async(d->m_multipartInputStream.get(), RunLoopSourcePriority::AsyncIONetwork,
988                 d->m_cancellable.get(), nextMultipartResponsePartCallback, handle.get());
989             return;
990         }
991
992         g_input_stream_close(d->m_inputStream.get(), 0, 0);
993
994         handle->client()->didFinishLoading(handle.get());
995         cleanupSoupRequestOperation(handle.get());
996         return;
997     }
998
999     // It's mandatory to have sent a response before sending data
1000     ASSERT(!d->m_response.isNull());
1001
1002     size_t currentPosition = handle->currentStreamPosition();
1003     size_t encodedDataLength = currentPosition ? currentPosition - d->m_previousPosition : bytesRead;
1004
1005     ASSERT(d->m_soupBuffer);
1006     d->m_soupBuffer->length = bytesRead; // The buffer might be larger than the number of bytes read. SharedBuffer looks at the length property.
1007     handle->client()->didReceiveBuffer(handle.get(), SharedBuffer::wrapSoupBuffer(d->m_soupBuffer.release()), encodedDataLength);
1008
1009     d->m_previousPosition = currentPosition;
1010
1011     // didReceiveBuffer may cancel the load, which may release the last reference.
1012     if (handle->cancelledOrClientless()) {
1013         cleanupSoupRequestOperation(handle.get());
1014         return;
1015     }
1016
1017     handle->ensureReadBuffer();
1018     g_input_stream_read_async(d->m_inputStream.get(), const_cast<char*>(d->m_soupBuffer->data), d->m_soupBuffer->length, RunLoopSourcePriority::AsyncIONetwork,
1019         d->m_cancellable.get(), readCallback, handle.get());
1020 }
1021
1022 void ResourceHandle::continueDidReceiveResponse()
1023 {
1024     continueAfterDidReceiveResponse(this);
1025 }
1026
1027 }
1028
1029 #endif