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 "CookieJarSoup.h"
35#include "CredentialStorage.h"
36#include "FileSystem.h"
37#include "GUniquePtrSoup.h"
38#include "HTTPParsers.h"
39#include "LocalizedStrings.h"
40#include "MIMETypeRegistry.h"
41#include "NetworkingContext.h"
42#include "NotImplemented.h"
43#include "ResourceError.h"
44#include "ResourceHandleClient.h"
45#include "ResourceHandleInternal.h"
46#include "ResourceResponse.h"
47#include "SharedBuffer.h"
48#include "SoupNetworkSession.h"
49#include "TextEncoding.h"
50#include <errno.h>
51#include <fcntl.h>
52#include <gio/gio.h>
53#include <glib.h>
54#include <libsoup/soup.h>
55#include <sys/stat.h>
56#include <sys/types.h>
57#if !COMPILER(MSVC)
58#include <unistd.h>
59#endif
60#include <wtf/CurrentTime.h>
61#include <wtf/SHA1.h>
62#include <wtf/gobject/GRefPtr.h>
63#include <wtf/text/Base64.h>
64#include <wtf/text/CString.h>
65
66#include "BlobData.h"
67#include "BlobRegistryImpl.h"
68
69#if PLATFORM(GTK)
70#include "CredentialBackingStore.h"
71#endif
72
73namespace WebCore {
74
75static bool loadingSynchronousRequest = false;
76static const size_t gDefaultReadBufferSize = 8192;
77
78class WebCoreSynchronousLoader : public ResourceHandleClient {
79    WTF_MAKE_NONCOPYABLE(WebCoreSynchronousLoader);
80public:
81
82    WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, SoupSession* session, Vector<char>& data, StoredCredentials storedCredentials)
83        : m_error(error)
84        , m_response(response)
85        , m_session(session)
86        , m_data(data)
87        , m_finished(false)
88        , m_storedCredentials(storedCredentials)
89
90    {
91        // We don't want any timers to fire while we are doing our synchronous load
92        // so we replace the thread default main context. The main loop iterations
93        // will only process GSources associated with this inner context.
94        loadingSynchronousRequest = true;
95        GRefPtr<GMainContext> innerMainContext = adoptGRef(g_main_context_new());
96        g_main_context_push_thread_default(innerMainContext.get());
97        m_mainLoop = adoptGRef(g_main_loop_new(innerMainContext.get(), false));
98
99        adjustMaxConnections(1);
100    }
101
102    ~WebCoreSynchronousLoader()
103    {
104        adjustMaxConnections(-1);
105
106        GMainContext* context = g_main_context_get_thread_default();
107        while (g_main_context_pending(context))
108            g_main_context_iteration(context, FALSE);
109
110        g_main_context_pop_thread_default(context);
111        loadingSynchronousRequest = false;
112    }
113
114    void adjustMaxConnections(int adjustment)
115    {
116        int maxConnections, maxConnectionsPerHost;
117        g_object_get(m_session,
118                     SOUP_SESSION_MAX_CONNS, &maxConnections,
119                     SOUP_SESSION_MAX_CONNS_PER_HOST, &maxConnectionsPerHost,
120                     NULL);
121        maxConnections += adjustment;
122        maxConnectionsPerHost += adjustment;
123        g_object_set(m_session,
124                     SOUP_SESSION_MAX_CONNS, maxConnections,
125                     SOUP_SESSION_MAX_CONNS_PER_HOST, maxConnectionsPerHost,
126                     NULL);
127
128    }
129
130    virtual bool isSynchronousClient()
131    {
132        return true;
133    }
134
135    virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
136    {
137        m_response = response;
138    }
139
140    virtual void didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int)
141    {
142        ASSERT_NOT_REACHED();
143    }
144
145    virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */)
146    {
147        // This pattern is suggested by SharedBuffer.h.
148        const char* segment;
149        unsigned position = 0;
150        while (unsigned length = buffer->getSomeData(segment, position)) {
151            m_data.append(segment, length);
152            position += length;
153        }
154    }
155
156    virtual void didFinishLoading(ResourceHandle*, double)
157    {
158        if (g_main_loop_is_running(m_mainLoop.get()))
159            g_main_loop_quit(m_mainLoop.get());
160        m_finished = true;
161    }
162
163    virtual void didFail(ResourceHandle* handle, const ResourceError& error)
164    {
165        m_error = error;
166        didFinishLoading(handle, 0);
167    }
168
169    virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge)
170    {
171        // We do not handle authentication for synchronous XMLHttpRequests.
172        challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge);
173    }
174
175    virtual bool shouldUseCredentialStorage(ResourceHandle*)
176    {
177        return m_storedCredentials == AllowStoredCredentials;
178    }
179
180    void run()
181    {
182        if (!m_finished)
183            g_main_loop_run(m_mainLoop.get());
184    }
185
186private:
187    ResourceError& m_error;
188    ResourceResponse& m_response;
189    SoupSession* m_session;
190    Vector<char>& m_data;
191    bool m_finished;
192    GRefPtr<GMainLoop> m_mainLoop;
193    StoredCredentials m_storedCredentials;
194};
195
196class HostTLSCertificateSet {
197public:
198    void add(GTlsCertificate* certificate)
199    {
200        String certificateHash = computeCertificateHash(certificate);
201        if (!certificateHash.isEmpty())
202            m_certificates.add(certificateHash);
203    }
204
205    bool contains(GTlsCertificate* certificate)
206    {
207        return m_certificates.contains(computeCertificateHash(certificate));
208    }
209
210private:
211    static String computeCertificateHash(GTlsCertificate* certificate)
212    {
213        GRefPtr<GByteArray> certificateData;
214        g_object_get(G_OBJECT(certificate), "certificate", &certificateData.outPtr(), NULL);
215        if (!certificateData)
216            return String();
217
218        SHA1 sha1;
219        sha1.addBytes(certificateData->data, certificateData->len);
220
221        SHA1::Digest digest;
222        sha1.computeHash(digest);
223
224        return base64Encode(reinterpret_cast<const char*>(digest.data()), SHA1::hashSize);
225    }
226
227    HashSet<String> m_certificates;
228};
229
230static bool createSoupRequestAndMessageForHandle(ResourceHandle*, const ResourceRequest&, bool isHTTPFamilyRequest);
231static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying = false);
232static void sendRequestCallback(GObject*, GAsyncResult*, gpointer);
233static void readCallback(GObject*, GAsyncResult*, gpointer);
234static gboolean requestTimeoutCallback(void*);
235#if ENABLE(WEB_TIMING)
236static int  milisecondsSinceRequest(double requestTime);
237#endif
238static void continueAfterDidReceiveResponse(ResourceHandle*);
239
240static bool gIgnoreSSLErrors = false;
241
242static HashSet<String>& allowsAnyHTTPSCertificateHosts()
243{
244    DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, hosts, ());
245    return hosts;
246}
247
248typedef HashMap<String, HostTLSCertificateSet> CertificatesMap;
249static CertificatesMap& clientCertificates()
250{
251    DEPRECATED_DEFINE_STATIC_LOCAL(CertificatesMap, certificates, ());
252    return certificates;
253}
254
255ResourceHandleInternal::~ResourceHandleInternal()
256{
257}
258
259static SoupSession* sessionFromContext(NetworkingContext* context)
260{
261    if (!context || !context->isValid())
262        return SoupNetworkSession::defaultSession().soupSession();
263    return context->storageSession().soupNetworkSession().soupSession();
264}
265
266ResourceHandle::~ResourceHandle()
267{
268    cleanupSoupRequestOperation(this, true);
269}
270
271SoupSession* ResourceHandleInternal::soupSession()
272{
273    return sessionFromContext(m_context.get());
274}
275
276bool ResourceHandle::cancelledOrClientless()
277{
278    if (!client())
279        return true;
280
281    return getInternal()->m_cancelled;
282}
283
284void ResourceHandle::ensureReadBuffer()
285{
286    ResourceHandleInternal* d = getInternal();
287
288    if (d->m_soupBuffer)
289        return;
290
291    // Non-NetworkProcess clients are able to give a buffer to the ResourceHandle to avoid expensive copies. If
292    // we do get a buffer from the client, we want the client to free it, so we create the soup buffer with
293    // SOUP_MEMORY_TEMPORARY.
294    size_t bufferSize;
295    char* bufferFromClient = client()->getOrCreateReadBuffer(gDefaultReadBufferSize, bufferSize);
296    if (bufferFromClient)
297        d->m_soupBuffer.reset(soup_buffer_new(SOUP_MEMORY_TEMPORARY, bufferFromClient, bufferSize));
298    else
299        d->m_soupBuffer.reset(soup_buffer_new(SOUP_MEMORY_TAKE, static_cast<char*>(g_malloc(gDefaultReadBufferSize)), gDefaultReadBufferSize));
300
301    ASSERT(d->m_soupBuffer);
302}
303
304static bool isAuthenticationFailureStatusCode(int httpStatusCode)
305{
306    return httpStatusCode == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || httpStatusCode == SOUP_STATUS_UNAUTHORIZED;
307}
308
309static void gotHeadersCallback(SoupMessage* message, gpointer data)
310{
311    ResourceHandle* handle = static_cast<ResourceHandle*>(data);
312    if (!handle || handle->cancelledOrClientless())
313        return;
314
315    ResourceHandleInternal* d = handle->getInternal();
316
317#if PLATFORM(GTK)
318    // We are a bit more conservative with the persistent credential storage than the session store,
319    // since we are waiting until we know that this authentication succeeded before actually storing.
320    // This is because we want to avoid hitting the disk twice (once to add and once to remove) for
321    // incorrect credentials or polluting the keychain with invalid credentials.
322    if (!isAuthenticationFailureStatusCode(message->status_code) && message->status_code < 500 && !d->m_credentialDataToSaveInPersistentStore.credential.isEmpty()) {
323        credentialBackingStore().storeCredentialsForChallenge(
324            d->m_credentialDataToSaveInPersistentStore.challenge,
325            d->m_credentialDataToSaveInPersistentStore.credential);
326    }
327#endif
328
329    // The original response will be needed later to feed to willSendRequest in
330    // doRedirect() in case we are redirected. For this reason, we store it here.
331    d->m_response.updateFromSoupMessage(message);
332}
333
334static void applyAuthenticationToRequest(ResourceHandle* handle, ResourceRequest& request, bool redirect)
335{
336    // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
337    ResourceHandleInternal* d = handle->getInternal();
338
339    if (handle->shouldUseCredentialStorage()) {
340        if (d->m_user.isEmpty() && d->m_pass.isEmpty())
341            d->m_initialCredential = CredentialStorage::get(request.url());
342        else if (!redirect) {
343            // If there is already a protection space known for the URL, update stored credentials
344            // before sending a request. This makes it possible to implement logout by sending an
345            // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
346            // an authentication dialog doesn't pop up).
347            CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
348        }
349    }
350
351    String user = d->m_user;
352    String password = d->m_pass;
353    if (!d->m_initialCredential.isEmpty()) {
354        user = d->m_initialCredential.user();
355        password = d->m_initialCredential.password();
356    }
357
358    if (user.isEmpty() && password.isEmpty()) {
359        // In case credential is not available from the handle and credential storage should not to be used,
360        // disable authentication manager so that credentials stored in libsoup are not used.
361        d->m_useAuthenticationManager = handle->shouldUseCredentialStorage();
362        return;
363    }
364
365    // We always put the credentials into the URL. In the CFNetwork-port HTTP family credentials are applied in
366    // the didReceiveAuthenticationChallenge callback, but libsoup requires us to use this method to override
367    // any previously remembered credentials. It has its own per-session credential storage.
368    URL urlWithCredentials(request.url());
369    urlWithCredentials.setUser(user);
370    urlWithCredentials.setPass(password);
371    request.setURL(urlWithCredentials);
372}
373
374#if ENABLE(WEB_TIMING)
375// Called each time the message is going to be sent again except the first time.
376// This happens when libsoup handles HTTP authentication.
377static void restartedCallback(SoupMessage*, gpointer data)
378{
379    ResourceHandle* handle = static_cast<ResourceHandle*>(data);
380    if (!handle || handle->cancelledOrClientless())
381        return;
382
383    handle->m_requestTime = monotonicallyIncreasingTime();
384}
385#endif
386
387static bool shouldRedirect(ResourceHandle* handle)
388{
389    ResourceHandleInternal* d = handle->getInternal();
390    SoupMessage* message = d->m_soupMessage.get();
391
392    // Some 3xx status codes aren't actually redirects.
393    if (message->status_code == 300 || message->status_code == 304 || message->status_code == 305 || message->status_code == 306)
394        return false;
395
396    if (!soup_message_headers_get_one(message->response_headers, "Location"))
397        return false;
398
399    return true;
400}
401
402static bool shouldRedirectAsGET(SoupMessage* message, URL& newURL, bool crossOrigin)
403{
404    if (message->method == SOUP_METHOD_GET || message->method == SOUP_METHOD_HEAD)
405        return false;
406
407    if (!newURL.protocolIsInHTTPFamily())
408        return true;
409
410    switch (message->status_code) {
411    case SOUP_STATUS_SEE_OTHER:
412        return true;
413    case SOUP_STATUS_FOUND:
414    case SOUP_STATUS_MOVED_PERMANENTLY:
415        if (message->method == SOUP_METHOD_POST)
416            return true;
417        break;
418    }
419
420    if (crossOrigin && message->method == SOUP_METHOD_DELETE)
421        return true;
422
423    return false;
424}
425
426static void continueAfterWillSendRequest(ResourceHandle* handle, const ResourceRequest& request)
427{
428    // willSendRequest might cancel the load.
429    if (handle->cancelledOrClientless())
430        return;
431
432    ResourceRequest newRequest(request);
433    ResourceHandleInternal* d = handle->getInternal();
434    if (protocolHostAndPortAreEqual(newRequest.url(), d->m_response.url()))
435        applyAuthenticationToRequest(handle, newRequest, true);
436
437    if (!createSoupRequestAndMessageForHandle(handle, newRequest, true)) {
438        d->client()->cannotShowURL(handle);
439        return;
440    }
441
442    handle->sendPendingRequest();
443}
444
445static void doRedirect(ResourceHandle* handle)
446{
447    ResourceHandleInternal* d = handle->getInternal();
448    static const int maxRedirects = 20;
449
450    if (d->m_redirectCount++ > maxRedirects) {
451        d->client()->didFail(handle, ResourceError::transportError(d->m_soupRequest.get(), SOUP_STATUS_TOO_MANY_REDIRECTS, "Too many redirects"));
452        cleanupSoupRequestOperation(handle);
453        return;
454    }
455
456    ResourceRequest newRequest = handle->firstRequest();
457    SoupMessage* message = d->m_soupMessage.get();
458    const char* location = soup_message_headers_get_one(message->response_headers, "Location");
459    URL newURL = URL(URL(soup_message_get_uri(message)), location);
460    bool crossOrigin = !protocolHostAndPortAreEqual(handle->firstRequest().url(), newURL);
461    newRequest.setURL(newURL);
462    newRequest.setFirstPartyForCookies(newURL);
463
464    if (newRequest.httpMethod() != "GET") {
465        // Change newRequest method to GET if change was made during a previous redirection
466        // or if current redirection says so
467        if (message->method == SOUP_METHOD_GET || shouldRedirectAsGET(message, newURL, crossOrigin)) {
468            newRequest.setHTTPMethod("GET");
469            newRequest.setHTTPBody(0);
470            newRequest.clearHTTPContentType();
471        }
472    }
473
474    // Should not set Referer after a redirect from a secure resource to non-secure one.
475    if (!newURL.protocolIs("https") && protocolIs(newRequest.httpReferrer(), "https") && handle->context()->shouldClearReferrerOnHTTPSToHTTPRedirect())
476        newRequest.clearHTTPReferrer();
477
478    d->m_user = newURL.user();
479    d->m_pass = newURL.pass();
480    newRequest.removeCredentials();
481
482    if (crossOrigin) {
483        // If the network layer carries over authentication headers from the original request
484        // in a cross-origin redirect, we want to clear those headers here.
485        newRequest.clearHTTPAuthorization();
486
487        // TODO: We are losing any username and password specified in the redirect URL, as this is the
488        // same behavior as the CFNet port. We should investigate if this is really what we want.
489    }
490
491    cleanupSoupRequestOperation(handle);
492
493    if (d->client()->usesAsyncCallbacks())
494        d->client()->willSendRequestAsync(handle, newRequest, d->m_response);
495    else {
496        d->client()->willSendRequest(handle, newRequest, d->m_response);
497        continueAfterWillSendRequest(handle, newRequest);
498    }
499
500}
501
502static void redirectSkipCallback(GObject*, GAsyncResult* asyncResult, gpointer data)
503{
504    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
505
506    if (handle->cancelledOrClientless()) {
507        cleanupSoupRequestOperation(handle.get());
508        return;
509    }
510
511    GUniqueOutPtr<GError> error;
512    ResourceHandleInternal* d = handle->getInternal();
513    gssize bytesSkipped = g_input_stream_skip_finish(d->m_inputStream.get(), asyncResult, &error.outPtr());
514    if (error) {
515        handle->client()->didFail(handle.get(), ResourceError::genericGError(error.get(), d->m_soupRequest.get()));
516        cleanupSoupRequestOperation(handle.get());
517        return;
518    }
519
520    if (bytesSkipped > 0) {
521        g_input_stream_skip_async(d->m_inputStream.get(), gDefaultReadBufferSize, G_PRIORITY_DEFAULT,
522            d->m_cancellable.get(), redirectSkipCallback, handle.get());
523        return;
524    }
525
526    g_input_stream_close(d->m_inputStream.get(), 0, 0);
527    doRedirect(handle.get());
528}
529
530static void wroteBodyDataCallback(SoupMessage*, SoupBuffer* buffer, gpointer data)
531{
532    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
533    if (!handle)
534        return;
535
536    ASSERT(buffer);
537    ResourceHandleInternal* d = handle->getInternal();
538    d->m_bodyDataSent += buffer->length;
539
540    if (handle->cancelledOrClientless())
541        return;
542
543    handle->client()->didSendData(handle.get(), d->m_bodyDataSent, d->m_bodySize);
544}
545
546static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroying)
547{
548    ResourceHandleInternal* d = handle->getInternal();
549
550    d->m_soupRequest.clear();
551    d->m_inputStream.clear();
552    d->m_multipartInputStream.clear();
553    d->m_cancellable.clear();
554    d->m_soupBuffer.reset();
555
556    if (d->m_soupMessage) {
557        g_signal_handlers_disconnect_matched(d->m_soupMessage.get(), G_SIGNAL_MATCH_DATA,
558                                             0, 0, 0, 0, handle);
559        g_object_set_data(G_OBJECT(d->m_soupMessage.get()), "handle", 0);
560        d->m_soupMessage.clear();
561    }
562
563    if (d->m_timeoutSource) {
564        g_source_destroy(d->m_timeoutSource.get());
565        d->m_timeoutSource.clear();
566    }
567
568    if (!isDestroying)
569        handle->deref();
570}
571
572static bool handleUnignoredTLSErrors(ResourceHandle* handle)
573{
574    ResourceHandleInternal* d = handle->getInternal();
575    const ResourceResponse& response = d->m_response;
576
577    if (!response.soupMessageTLSErrors() || gIgnoreSSLErrors)
578        return false;
579
580    String lowercaseHostURL = handle->firstRequest().url().host().lower();
581    if (allowsAnyHTTPSCertificateHosts().contains(lowercaseHostURL))
582        return false;
583
584    // We aren't ignoring errors globally, but the user may have already decided to accept this certificate.
585    CertificatesMap::iterator i = clientCertificates().find(lowercaseHostURL);
586    if (i != clientCertificates().end() && i->value.contains(response.soupMessageCertificate()))
587        return false;
588
589    handle->client()->didFail(handle, ResourceError::tlsError(d->m_soupRequest.get(), response.soupMessageTLSErrors(), response.soupMessageCertificate()));
590    return true;
591}
592
593size_t ResourceHandle::currentStreamPosition() const
594{
595    GInputStream* baseStream = d->m_inputStream.get();
596    while (!G_IS_SEEKABLE(baseStream) && G_IS_FILTER_INPUT_STREAM(baseStream))
597        baseStream = g_filter_input_stream_get_base_stream(G_FILTER_INPUT_STREAM(baseStream));
598
599    if (!G_IS_SEEKABLE(baseStream))
600        return 0;
601
602    return g_seekable_tell(G_SEEKABLE(baseStream));
603}
604
605static void nextMultipartResponsePartCallback(GObject* /*source*/, GAsyncResult* result, gpointer data)
606{
607    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
608
609    if (handle->cancelledOrClientless()) {
610        cleanupSoupRequestOperation(handle.get());
611        return;
612    }
613
614    ResourceHandleInternal* d = handle->getInternal();
615    ASSERT(!d->m_inputStream);
616
617    GUniqueOutPtr<GError> error;
618    d->m_inputStream = adoptGRef(soup_multipart_input_stream_next_part_finish(d->m_multipartInputStream.get(), result, &error.outPtr()));
619
620    if (error) {
621        handle->client()->didFail(handle.get(), ResourceError::httpError(d->m_soupMessage.get(), error.get(), d->m_soupRequest.get()));
622        cleanupSoupRequestOperation(handle.get());
623        return;
624    }
625
626    if (!d->m_inputStream) {
627        handle->client()->didFinishLoading(handle.get(), 0);
628        cleanupSoupRequestOperation(handle.get());
629        return;
630    }
631
632    d->m_response = ResourceResponse();
633    d->m_response.setURL(handle->firstRequest().url());
634    d->m_response.updateFromSoupMessageHeaders(soup_multipart_input_stream_get_headers(d->m_multipartInputStream.get()));
635
636    d->m_previousPosition = 0;
637
638    if (handle->client()->usesAsyncCallbacks())
639        handle->client()->didReceiveResponseAsync(handle.get(), d->m_response);
640    else {
641        handle->client()->didReceiveResponse(handle.get(), d->m_response);
642        continueAfterDidReceiveResponse(handle.get());
643    }
644}
645
646static void sendRequestCallback(GObject*, GAsyncResult* result, gpointer data)
647{
648    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
649
650    if (handle->cancelledOrClientless()) {
651        cleanupSoupRequestOperation(handle.get());
652        return;
653    }
654
655    ResourceHandleInternal* d = handle->getInternal();
656    SoupMessage* soupMessage = d->m_soupMessage.get();
657
658
659    if (d->m_defersLoading) {
660        d->m_deferredResult = result;
661        return;
662    }
663
664    GUniqueOutPtr<GError> error;
665    GRefPtr<GInputStream> inputStream = adoptGRef(soup_request_send_finish(d->m_soupRequest.get(), result, &error.outPtr()));
666    if (error) {
667        handle->client()->didFail(handle.get(), ResourceError::httpError(soupMessage, error.get(), d->m_soupRequest.get()));
668        cleanupSoupRequestOperation(handle.get());
669        return;
670    }
671
672    if (soupMessage) {
673        if (handle->shouldContentSniff() && soupMessage->status_code != SOUP_STATUS_NOT_MODIFIED) {
674            const char* sniffedType = soup_request_get_content_type(d->m_soupRequest.get());
675            d->m_response.setSniffedContentType(sniffedType);
676        }
677        d->m_response.updateFromSoupMessage(soupMessage);
678
679        if (handleUnignoredTLSErrors(handle.get())) {
680            cleanupSoupRequestOperation(handle.get());
681            return;
682        }
683
684        if (SOUP_STATUS_IS_REDIRECTION(soupMessage->status_code) && shouldRedirect(handle.get())) {
685            d->m_inputStream = inputStream;
686            g_input_stream_skip_async(d->m_inputStream.get(), gDefaultReadBufferSize, G_PRIORITY_DEFAULT,
687                d->m_cancellable.get(), redirectSkipCallback, handle.get());
688            return;
689        }
690    } else {
691        d->m_response.setURL(handle->firstRequest().url());
692        const gchar* contentType = soup_request_get_content_type(d->m_soupRequest.get());
693        d->m_response.setMimeType(extractMIMETypeFromMediaType(contentType));
694        d->m_response.setTextEncodingName(extractCharsetFromMediaType(contentType));
695        d->m_response.setExpectedContentLength(soup_request_get_content_length(d->m_soupRequest.get()));
696    }
697
698#if ENABLE(WEB_TIMING)
699    d->m_response.resourceLoadTiming().responseStart = milisecondsSinceRequest(handle->m_requestTime);
700#endif
701
702    if (soupMessage && d->m_response.isMultipart())
703        d->m_multipartInputStream = adoptGRef(soup_multipart_input_stream_new(soupMessage, inputStream.get()));
704    else
705        d->m_inputStream = inputStream;
706
707    if (d->client()->usesAsyncCallbacks())
708        handle->client()->didReceiveResponseAsync(handle.get(), d->m_response);
709    else {
710        handle->client()->didReceiveResponse(handle.get(), d->m_response);
711        continueAfterDidReceiveResponse(handle.get());
712    }
713}
714
715static void continueAfterDidReceiveResponse(ResourceHandle* handle)
716{
717    if (handle->cancelledOrClientless()) {
718        cleanupSoupRequestOperation(handle);
719        return;
720    }
721
722    ResourceHandleInternal* d = handle->getInternal();
723    if (d->m_soupMessage && d->m_multipartInputStream && !d->m_inputStream) {
724        soup_multipart_input_stream_next_part_async(d->m_multipartInputStream.get(), G_PRIORITY_DEFAULT,
725            d->m_cancellable.get(), nextMultipartResponsePartCallback, handle);
726        return;
727    }
728
729    ASSERT(d->m_inputStream);
730    handle->ensureReadBuffer();
731    g_input_stream_read_async(d->m_inputStream.get(), const_cast<char*>(d->m_soupBuffer->data), d->m_soupBuffer->length,
732        G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, handle);
733}
734
735static bool addFileToSoupMessageBody(SoupMessage* message, const String& fileNameString, size_t offset, size_t lengthToSend, unsigned long& totalBodySize)
736{
737    GUniqueOutPtr<GError> error;
738    CString fileName = fileSystemRepresentation(fileNameString);
739    GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr());
740    if (error)
741        return false;
742
743    gsize bufferLength = lengthToSend;
744    if (!lengthToSend)
745        bufferLength = g_mapped_file_get_length(fileMapping);
746    totalBodySize += bufferLength;
747
748    SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping) + offset,
749                                                        bufferLength,
750                                                        fileMapping,
751                                                        reinterpret_cast<GDestroyNotify>(g_mapped_file_unref));
752    soup_message_body_append_buffer(message->request_body, soupBuffer);
753    soup_buffer_free(soupBuffer);
754    return true;
755}
756
757static bool blobIsOutOfDate(const BlobDataItem& blobItem)
758{
759    ASSERT(blobItem.type == BlobDataItem::File);
760    if (!isValidFileTime(blobItem.file->expectedModificationTime()))
761        return false;
762
763    time_t fileModificationTime;
764    if (!getFileModificationTime(blobItem.file->path(), fileModificationTime))
765        return true;
766
767    return fileModificationTime != static_cast<time_t>(blobItem.file->expectedModificationTime());
768}
769
770static void addEncodedBlobItemToSoupMessageBody(SoupMessage* message, const BlobDataItem& blobItem, unsigned long& totalBodySize)
771{
772    if (blobItem.type == BlobDataItem::Data) {
773        totalBodySize += blobItem.length();
774        soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY, blobItem.data->data() + blobItem.offset(), blobItem.length());
775        return;
776    }
777
778    ASSERT(blobItem.type == BlobDataItem::File);
779    if (blobIsOutOfDate(blobItem))
780        return;
781
782    addFileToSoupMessageBody(message, blobItem.file->path(), blobItem.offset(), blobItem.length() == BlobDataItem::toEndOfFile ? 0 : blobItem.length(),  totalBodySize);
783}
784
785static void addEncodedBlobToSoupMessageBody(SoupMessage* message, const FormDataElement& element, unsigned long& totalBodySize)
786{
787    RefPtr<BlobData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(URL(ParsedURLString, element.m_url));
788    if (!blobData)
789        return;
790
791    for (size_t i = 0; i < blobData->items().size(); ++i)
792        addEncodedBlobItemToSoupMessageBody(message, blobData->items()[i], totalBodySize);
793}
794
795static bool addFormElementsToSoupMessage(SoupMessage* message, const char*, FormData* httpBody, unsigned long& totalBodySize)
796{
797    soup_message_body_set_accumulate(message->request_body, FALSE);
798    size_t numElements = httpBody->elements().size();
799    for (size_t i = 0; i < numElements; i++) {
800        const FormDataElement& element = httpBody->elements()[i];
801
802        if (element.m_type == FormDataElement::Type::Data) {
803            totalBodySize += element.m_data.size();
804            soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY,
805                                     element.m_data.data(), element.m_data.size());
806            continue;
807        }
808
809        if (element.m_type == FormDataElement::Type::EncodedFile) {
810            if (!addFileToSoupMessageBody(message ,
811                                         element.m_filename,
812                                         0 /* offset */,
813                                         0 /* lengthToSend */,
814                                         totalBodySize))
815                return false;
816            continue;
817        }
818
819        ASSERT(element.m_type == FormDataElement::Type::EncodedBlob);
820        addEncodedBlobToSoupMessageBody(message, element, totalBodySize);
821    }
822    return true;
823}
824
825#if ENABLE(WEB_TIMING)
826static int milisecondsSinceRequest(double requestTime)
827{
828    return static_cast<int>((monotonicallyIncreasingTime() - requestTime) * 1000.0);
829}
830
831void ResourceHandle::didStartRequest()
832{
833    getInternal()->m_response.resourceLoadTiming().requestStart = milisecondsSinceRequest(m_requestTime);
834}
835
836static void networkEventCallback(SoupMessage*, GSocketClientEvent event, GIOStream*, gpointer data)
837{
838    ResourceHandle* handle = static_cast<ResourceHandle*>(data);
839    if (!handle)
840        return;
841
842    if (handle->cancelledOrClientless())
843        return;
844
845    ResourceHandleInternal* d = handle->getInternal();
846    int deltaTime = milisecondsSinceRequest(handle->m_requestTime);
847    switch (event) {
848    case G_SOCKET_CLIENT_RESOLVING:
849        d->m_response.resourceLoadTiming().domainLookupStart = deltaTime;
850        break;
851    case G_SOCKET_CLIENT_RESOLVED:
852        d->m_response.resourceLoadTiming().domainLookupEnd = deltaTime;
853        break;
854    case G_SOCKET_CLIENT_CONNECTING:
855        d->m_response.resourceLoadTiming().connectStart = deltaTime;
856        if (d->m_response.resourceLoadTiming().domainLookupStart != -1) {
857            // WebCore/inspector/front-end/RequestTimingView.js assumes
858            // that DNS time is included in connection time so must
859            // substract here the DNS delta that will be added later (see
860            // WebInspector.RequestTimingView.createTimingTable in the
861            // file above for more details).
862            d->m_response.resourceLoadTiming().connectStart -=
863                d->m_response.resourceLoadTiming().domainLookupEnd - d->m_response.resourceLoadTiming().domainLookupStart;
864        }
865        break;
866    case G_SOCKET_CLIENT_CONNECTED:
867        // Web Timing considers that connection time involves dns, proxy & TLS negotiation...
868        // so we better pick G_SOCKET_CLIENT_COMPLETE for connectEnd
869        break;
870    case G_SOCKET_CLIENT_PROXY_NEGOTIATING:
871        break;
872    case G_SOCKET_CLIENT_PROXY_NEGOTIATED:
873        break;
874    case G_SOCKET_CLIENT_TLS_HANDSHAKING:
875        d->m_response.resourceLoadTiming().secureConnectionStart = deltaTime;
876        break;
877    case G_SOCKET_CLIENT_TLS_HANDSHAKED:
878        break;
879    case G_SOCKET_CLIENT_COMPLETE:
880        d->m_response.resourceLoadTiming().connectEnd = deltaTime;
881        break;
882    default:
883        ASSERT_NOT_REACHED();
884        break;
885    }
886}
887#endif
888
889static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const ResourceRequest& request)
890{
891    ASSERT(handle);
892
893    ResourceHandleInternal* d = handle->getInternal();
894    ASSERT(d->m_soupRequest);
895
896    d->m_soupMessage = adoptGRef(soup_request_http_get_message(SOUP_REQUEST_HTTP(d->m_soupRequest.get())));
897    if (!d->m_soupMessage)
898        return false;
899
900    SoupMessage* soupMessage = d->m_soupMessage.get();
901    request.updateSoupMessage(soupMessage);
902
903    g_object_set_data(G_OBJECT(soupMessage), "handle", handle);
904    if (!handle->shouldContentSniff())
905        soup_message_disable_feature(soupMessage, SOUP_TYPE_CONTENT_SNIFFER);
906    if (!d->m_useAuthenticationManager)
907        soup_message_disable_feature(soupMessage, SOUP_TYPE_AUTH_MANAGER);
908
909    FormData* httpBody = request.httpBody();
910    CString contentType = request.httpContentType().utf8().data();
911    if (httpBody && !httpBody->isEmpty() && !addFormElementsToSoupMessage(soupMessage, contentType.data(), httpBody, d->m_bodySize)) {
912        // We failed to prepare the body data, so just fail this load.
913        d->m_soupMessage.clear();
914        return false;
915    }
916
917    // Make sure we have an Accept header for subresources; some sites
918    // want this to serve some of their subresources
919    if (!soup_message_headers_get_one(soupMessage->request_headers, "Accept"))
920        soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*");
921
922    // In the case of XHR .send() and .send("") explicitly tell libsoup to send a zero content-lenght header
923    // for consistency with other backends (e.g. Chromium's) and other UA implementations like FF. It's done
924    // in the backend here instead of in XHR code since in XHR CORS checking prevents us from this kind of
925    // late header manipulation.
926    if ((request.httpMethod() == "POST" || request.httpMethod() == "PUT")
927        && (!request.httpBody() || request.httpBody()->isEmpty()))
928        soup_message_headers_set_content_length(soupMessage->request_headers, 0);
929
930    g_signal_connect(d->m_soupMessage.get(), "got-headers", G_CALLBACK(gotHeadersCallback), handle);
931    g_signal_connect(d->m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle);
932
933    soup_message_set_flags(d->m_soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(d->m_soupMessage.get()) | SOUP_MESSAGE_NO_REDIRECT));
934
935#if ENABLE(WEB_TIMING)
936    g_signal_connect(d->m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), handle);
937    g_signal_connect(d->m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), handle);
938#endif
939
940#if SOUP_CHECK_VERSION(2, 43, 1)
941    soup_message_set_priority(d->m_soupMessage.get(), toSoupMessagePriority(request.priority()));
942#endif
943
944    return true;
945}
946
947static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const ResourceRequest& request, bool isHTTPFamilyRequest)
948{
949    ResourceHandleInternal* d = handle->getInternal();
950
951    GUniquePtr<SoupURI> soupURI = request.createSoupURI();
952    if (!soupURI)
953        return false;
954
955    GUniqueOutPtr<GError> error;
956    d->m_soupRequest = adoptGRef(soup_session_request_uri(d->soupSession(), soupURI.get(), &error.outPtr()));
957    if (error) {
958        d->m_soupRequest.clear();
959        return false;
960    }
961
962    // SoupMessages are only applicable to HTTP-family requests.
963    if (isHTTPFamilyRequest && !createSoupMessageForHandleAndRequest(handle, request)) {
964        d->m_soupRequest.clear();
965        return false;
966    }
967
968    request.updateSoupRequest(d->m_soupRequest.get());
969
970    return true;
971}
972
973bool ResourceHandle::start()
974{
975    ASSERT(!d->m_soupMessage);
976
977    // The frame could be null if the ResourceHandle is not associated to any
978    // Frame, e.g. if we are downloading a file.
979    // If the frame is not null but the page is null this must be an attempted
980    // load from an unload handler, so let's just block it.
981    // If both the frame and the page are not null the context is valid.
982    if (d->m_context && !d->m_context->isValid())
983        return false;
984
985    // Only allow the POST and GET methods for non-HTTP requests.
986    const ResourceRequest& request = firstRequest();
987    bool isHTTPFamilyRequest = request.url().protocolIsInHTTPFamily();
988    if (!isHTTPFamilyRequest && request.httpMethod() != "GET" && request.httpMethod() != "POST") {
989        this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
990        return true;
991    }
992
993    applyAuthenticationToRequest(this, firstRequest(), false);
994
995    if (!createSoupRequestAndMessageForHandle(this, request, isHTTPFamilyRequest)) {
996        this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
997        return true;
998    }
999
1000    // Send the request only if it's not been explicitly deferred.
1001    if (!d->m_defersLoading)
1002        sendPendingRequest();
1003
1004    return true;
1005}
1006
1007void ResourceHandle::sendPendingRequest()
1008{
1009#if ENABLE(WEB_TIMING)
1010    m_requestTime = monotonicallyIncreasingTime();
1011#endif
1012
1013    if (d->m_firstRequest.timeoutInterval() > 0) {
1014        // soup_add_timeout returns a GSource* whose only reference is owned by
1015        // the context. We need to have our own reference to it, hence not using adoptRef.
1016        d->m_timeoutSource = soup_add_timeout(g_main_context_get_thread_default(),
1017            d->m_firstRequest.timeoutInterval() * 1000, requestTimeoutCallback, this);
1018    }
1019
1020    // Balanced by a deref() in cleanupSoupRequestOperation, which should always run.
1021    ref();
1022
1023    d->m_cancellable = adoptGRef(g_cancellable_new());
1024    soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, this);
1025}
1026
1027void ResourceHandle::cancel()
1028{
1029    d->m_cancelled = true;
1030    if (d->m_soupMessage)
1031        soup_session_cancel_message(d->soupSession(), d->m_soupMessage.get(), SOUP_STATUS_CANCELLED);
1032    else if (d->m_cancellable)
1033        g_cancellable_cancel(d->m_cancellable.get());
1034}
1035
1036bool ResourceHandle::shouldUseCredentialStorage()
1037{
1038    return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
1039}
1040
1041void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
1042{
1043    allowsAnyHTTPSCertificateHosts().add(host.lower());
1044}
1045
1046void ResourceHandle::setClientCertificate(const String& host, GTlsCertificate* certificate)
1047{
1048    clientCertificates().add(host.lower(), HostTLSCertificateSet()).iterator->value.add(certificate);
1049}
1050
1051void ResourceHandle::setIgnoreSSLErrors(bool ignoreSSLErrors)
1052{
1053    gIgnoreSSLErrors = ignoreSSLErrors;
1054}
1055
1056#if PLATFORM(GTK)
1057void getCredentialFromPersistentStoreCallback(const Credential& credential, void* data)
1058{
1059    static_cast<ResourceHandle*>(data)->continueDidReceiveAuthenticationChallenge(credential);
1060}
1061#endif
1062
1063void ResourceHandle::continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage)
1064{
1065    ASSERT(!d->m_currentWebChallenge.isNull());
1066    AuthenticationChallenge& challenge = d->m_currentWebChallenge;
1067
1068    ASSERT(challenge.soupSession());
1069    ASSERT(challenge.soupMessage());
1070    if (!credentialFromPersistentStorage.isEmpty())
1071        challenge.setProposedCredential(credentialFromPersistentStorage);
1072
1073    if (!client()) {
1074        soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage());
1075        clearAuthentication();
1076        return;
1077    }
1078
1079    ASSERT(challenge.soupSession());
1080    ASSERT(challenge.soupMessage());
1081    client()->didReceiveAuthenticationChallenge(this, challenge);
1082}
1083
1084void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
1085{
1086    ASSERT(d->m_currentWebChallenge.isNull());
1087
1088    // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
1089    bool useCredentialStorage = shouldUseCredentialStorage();
1090    if (useCredentialStorage) {
1091        if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
1092            // The stored credential wasn't accepted, stop using it. There is a race condition
1093            // here, since a different credential might have already been stored by another
1094            // ResourceHandle, but the observable effect should be very minor, if any.
1095            CredentialStorage::remove(challenge.protectionSpace());
1096        }
1097
1098        if (!challenge.previousFailureCount()) {
1099            Credential credential = CredentialStorage::get(challenge.protectionSpace());
1100            if (!credential.isEmpty() && credential != d->m_initialCredential) {
1101                ASSERT(credential.persistence() == CredentialPersistenceNone);
1102
1103                // Store the credential back, possibly adding it as a default for this directory.
1104                if (isAuthenticationFailureStatusCode(challenge.failureResponse().httpStatusCode()))
1105                    CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
1106
1107                soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
1108                return;
1109            }
1110        }
1111    }
1112
1113    d->m_currentWebChallenge = challenge;
1114    soup_session_pause_message(challenge.soupSession(), challenge.soupMessage());
1115
1116#if PLATFORM(GTK)
1117    // We could also do this before we even start the request, but that would be at the expense
1118    // of all request latency, versus a one-time latency for the small subset of requests that
1119    // use HTTP authentication. In the end, this doesn't matter much, because persistent credentials
1120    // will become session credentials after the first use.
1121    if (useCredentialStorage) {
1122        credentialBackingStore().credentialForChallenge(challenge, getCredentialFromPersistentStoreCallback, this);
1123        return;
1124    }
1125#endif
1126
1127    continueDidReceiveAuthenticationChallenge(Credential());
1128}
1129
1130void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
1131{
1132    ASSERT(!challenge.isNull());
1133    if (challenge != d->m_currentWebChallenge)
1134        return;
1135    soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage());
1136
1137    clearAuthentication();
1138}
1139
1140void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
1141{
1142    ASSERT(!challenge.isNull());
1143    if (challenge != d->m_currentWebChallenge)
1144        return;
1145
1146    // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
1147    if (credential.isEmpty()) {
1148        receivedRequestToContinueWithoutCredential(challenge);
1149        return;
1150    }
1151
1152    if (shouldUseCredentialStorage()) {
1153        // Eventually we will manage per-session credentials only internally or use some newly-exposed API from libsoup,
1154        // because once we authenticate via libsoup, there is no way to ignore it for a particular request. Right now,
1155        // we place the credentials in the store even though libsoup will never fire the authenticate signal again for
1156        // this protection space.
1157        if (credential.persistence() == CredentialPersistenceForSession || credential.persistence() == CredentialPersistencePermanent)
1158            CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
1159
1160#if PLATFORM(GTK)
1161        if (credential.persistence() == CredentialPersistencePermanent) {
1162            d->m_credentialDataToSaveInPersistentStore.credential = credential;
1163            d->m_credentialDataToSaveInPersistentStore.challenge = challenge;
1164        }
1165#endif
1166    }
1167
1168    ASSERT(challenge.soupSession());
1169    ASSERT(challenge.soupMessage());
1170    soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
1171    soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage());
1172
1173    clearAuthentication();
1174}
1175
1176void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
1177{
1178    ASSERT(!challenge.isNull());
1179    if (challenge != d->m_currentWebChallenge)
1180        return;
1181
1182    if (cancelledOrClientless()) {
1183        clearAuthentication();
1184        return;
1185    }
1186
1187    ASSERT(challenge.soupSession());
1188    ASSERT(challenge.soupMessage());
1189    soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage());
1190
1191    if (client())
1192        client()->receivedCancellation(this, challenge);
1193
1194    clearAuthentication();
1195}
1196
1197void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&)
1198{
1199    ASSERT_NOT_REACHED();
1200}
1201
1202void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge&)
1203{
1204    ASSERT_NOT_REACHED();
1205}
1206
1207static bool waitingToSendRequest(ResourceHandle* handle)
1208{
1209    // We need to check for d->m_soupRequest because the request may have raised a failure
1210    // (for example invalid URLs). We cannot  simply check for d->m_scheduledFailure because
1211    // it's cleared as soon as the failure event is fired.
1212    return handle->getInternal()->m_soupRequest && !handle->getInternal()->m_cancellable;
1213}
1214
1215void ResourceHandle::platformSetDefersLoading(bool defersLoading)
1216{
1217    if (cancelledOrClientless())
1218        return;
1219
1220    // Except when canceling a possible timeout timer, we only need to take action here to UN-defer loading.
1221    if (defersLoading) {
1222        if (d->m_timeoutSource) {
1223            g_source_destroy(d->m_timeoutSource.get());
1224            d->m_timeoutSource.clear();
1225        }
1226        return;
1227    }
1228
1229    if (waitingToSendRequest(this)) {
1230        sendPendingRequest();
1231        return;
1232    }
1233
1234    if (d->m_deferredResult) {
1235        GRefPtr<GAsyncResult> asyncResult = adoptGRef(d->m_deferredResult.leakRef());
1236
1237        if (d->m_inputStream)
1238            readCallback(G_OBJECT(d->m_inputStream.get()), asyncResult.get(), this);
1239        else
1240            sendRequestCallback(G_OBJECT(d->m_soupRequest.get()), asyncResult.get(), this);
1241    }
1242}
1243
1244bool ResourceHandle::loadsBlocked()
1245{
1246    return false;
1247}
1248
1249void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
1250{
1251    ASSERT(!loadingSynchronousRequest);
1252    if (loadingSynchronousRequest) // In practice this cannot happen, but if for some reason it does,
1253        return;                    // we want to avoid accidentally going into an infinite loop of requests.
1254
1255    WebCoreSynchronousLoader syncLoader(error, response, sessionFromContext(context), data, storedCredentials);
1256    RefPtr<ResourceHandle> handle = create(context, request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/);
1257    if (!handle)
1258        return;
1259
1260    // If the request has already failed, do not run the main loop, or else we'll block indefinitely.
1261    if (handle->d->m_scheduledFailureType != NoFailure)
1262        return;
1263
1264    syncLoader.run();
1265}
1266
1267static void readCallback(GObject*, GAsyncResult* asyncResult, gpointer data)
1268{
1269    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
1270
1271    if (handle->cancelledOrClientless()) {
1272        cleanupSoupRequestOperation(handle.get());
1273        return;
1274    }
1275
1276    ResourceHandleInternal* d = handle->getInternal();
1277    if (d->m_defersLoading) {
1278        d->m_deferredResult = asyncResult;
1279        return;
1280    }
1281
1282    GUniqueOutPtr<GError> error;
1283    gssize bytesRead = g_input_stream_read_finish(d->m_inputStream.get(), asyncResult, &error.outPtr());
1284
1285    if (error) {
1286        handle->client()->didFail(handle.get(), ResourceError::genericGError(error.get(), d->m_soupRequest.get()));
1287        cleanupSoupRequestOperation(handle.get());
1288        return;
1289    }
1290
1291    if (!bytesRead) {
1292        // If this is a multipart message, we'll look for another part.
1293        if (d->m_soupMessage && d->m_multipartInputStream) {
1294            d->m_inputStream.clear();
1295            soup_multipart_input_stream_next_part_async(d->m_multipartInputStream.get(), G_PRIORITY_DEFAULT,
1296                d->m_cancellable.get(), nextMultipartResponsePartCallback, handle.get());
1297            return;
1298        }
1299
1300        g_input_stream_close(d->m_inputStream.get(), 0, 0);
1301
1302        handle->client()->didFinishLoading(handle.get(), 0);
1303        cleanupSoupRequestOperation(handle.get());
1304        return;
1305    }
1306
1307    // It's mandatory to have sent a response before sending data
1308    ASSERT(!d->m_response.isNull());
1309
1310    size_t currentPosition = handle->currentStreamPosition();
1311    size_t encodedDataLength = currentPosition ? currentPosition - d->m_previousPosition : bytesRead;
1312
1313    ASSERT(d->m_soupBuffer);
1314    d->m_soupBuffer->length = bytesRead; // The buffer might be larger than the number of bytes read. SharedBuffer looks at the length property.
1315    handle->client()->didReceiveBuffer(handle.get(), SharedBuffer::wrapSoupBuffer(d->m_soupBuffer.release()), encodedDataLength);
1316
1317    d->m_previousPosition = currentPosition;
1318
1319    // didReceiveBuffer may cancel the load, which may release the last reference.
1320    if (handle->cancelledOrClientless()) {
1321        cleanupSoupRequestOperation(handle.get());
1322        return;
1323    }
1324
1325    handle->ensureReadBuffer();
1326    g_input_stream_read_async(d->m_inputStream.get(), const_cast<char*>(d->m_soupBuffer->data), d->m_soupBuffer->length, G_PRIORITY_DEFAULT,
1327        d->m_cancellable.get(), readCallback, handle.get());
1328}
1329
1330void ResourceHandle::continueWillSendRequest(const ResourceRequest& request)
1331{
1332    ASSERT(client());
1333    ASSERT(client()->usesAsyncCallbacks());
1334    continueAfterWillSendRequest(this, request);
1335}
1336
1337void ResourceHandle::continueDidReceiveResponse()
1338{
1339    ASSERT(client());
1340    ASSERT(client()->usesAsyncCallbacks());
1341    continueAfterDidReceiveResponse(this);
1342}
1343
1344static gboolean requestTimeoutCallback(gpointer data)
1345{
1346    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
1347    handle->client()->didFail(handle.get(), ResourceError::timeoutError(handle->getInternal()->m_firstRequest.url().string()));
1348    handle->cancel();
1349
1350    return FALSE;
1351}
1352
1353}
1354
1355#endif
1356