Changeset 230019 in webkit


Ignore:
Timestamp:
Mar 27, 2018 6:36:52 PM (6 years ago)
Author:
Michael Catanzaro
Message:

Unreviewed, rolling out r230008.

Lots of HTTP test failures

Reverted changeset:

"Remove unused libsoup ResourceHandle implementation"
https://bugs.webkit.org/show_bug.cgi?id=184048
https://trac.webkit.org/changeset/230008

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r230017 r230019  
     12018-03-27  Michael Catanzaro  <mcatanzaro@igalia.com>
     2
     3        Unreviewed, rolling out r230008.
     4
     5        Lots of HTTP test failures
     6
     7        Reverted changeset:
     8
     9        "Remove unused libsoup ResourceHandle implementation"
     10        https://bugs.webkit.org/show_bug.cgi?id=184048
     11        https://trac.webkit.org/changeset/230008
     12
    1132018-03-27  Chris Dumez  <cdumez@apple.com>
    214
  • trunk/Source/WebCore/platform/network/ResourceHandle.h

    r230008 r230019  
    4141#endif
    4242
     43#if USE(SOUP)
     44typedef struct _GTlsCertificate GTlsCertificate;
     45typedef struct _SoupSession SoupSession;
     46typedef struct _SoupRequest SoupRequest;
     47#endif
     48
    4349#if USE(CF)
    4450typedef const struct __CFData * CFDataRef;
     
    8793class ResourceRequest;
    8894class ResourceResponse;
     95class SoupNetworkSession;
    8996class SharedBuffer;
    9097class Timer;
     
    99106    WEBCORE_EXPORT static RefPtr<ResourceHandle> create(NetworkingContext*, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff);
    100107    WEBCORE_EXPORT static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentialsPolicy, ResourceError&, ResourceResponse&, Vector<char>& data);
     108
     109#if USE(SOUP)
     110    static RefPtr<ResourceHandle> create(SoupNetworkSession&, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff);
     111#endif
     112
    101113    WEBCORE_EXPORT virtual ~ResourceHandle();
    102114
     
    168180    WEBCORE_EXPORT static void forceContentSniffing();
    169181
    170 #if USE(CURL)
     182#if USE(CURL) || USE(SOUP)
    171183    ResourceHandleInternal* getInternal() { return d.get(); }
     184#endif
     185
     186#if USE(SOUP)
     187    RefPtr<ResourceHandle> releaseForDownload(ResourceHandleClient*);
     188    void continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage);
     189    void sendPendingRequest();
     190    bool cancelledOrClientless();
     191    void ensureReadBuffer();
     192    size_t currentStreamPosition() const;
     193    void didStartRequest();
     194    MonotonicTime m_requestTime;
    172195#endif
    173196
     
    221244    };
    222245
     246#if USE(SOUP)
     247    ResourceHandle(SoupNetworkSession&, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff);
     248#endif
     249
    223250    void platformSetDefersLoading(bool);
    224251
     
    251278#if PLATFORM(COCOA)
    252279    void applySniffingPoliciesAndStoragePartitionIfNeeded(NSURLRequest*&, bool shouldContentSniff, bool shouldContentEncodingSniff);
     280#endif
     281
     282#if USE(SOUP)
     283    void timeoutFired();
    253284#endif
    254285
  • trunk/Source/WebCore/platform/network/ResourceHandleInternal.h

    r230008 r230019  
    4242#endif
    4343
     44#if USE(SOUP)
     45#include "GUniquePtrSoup.h"
     46#include "SoupNetworkSession.h"
     47#include <libsoup/soup.h>
     48#include <wtf/RunLoop.h>
     49#include <wtf/glib/GRefPtr.h>
     50#endif
     51
    4452#if PLATFORM(COCOA)
    4553OBJC_CLASS NSURLAuthenticationChallenge;
     
    7179#if USE(CFURLCONNECTION)
    7280        , m_currentRequest(request)
     81#endif
     82#if USE(SOUP)
     83        , m_timeoutSource(RunLoop::main(), loader, &ResourceHandle::timeoutFired)
    7384#endif
    7485        , m_failureTimer(*loader, &ResourceHandle::failureTimerFired)
     
    126137#endif
    127138
     139#if USE(SOUP)
     140    SoupNetworkSession* m_session { nullptr };
     141    GRefPtr<SoupMessage> m_soupMessage;
     142    ResourceResponse m_response;
     143    bool m_cancelled { false };
     144    GRefPtr<SoupRequest> m_soupRequest;
     145    GRefPtr<GInputStream> m_inputStream;
     146    GRefPtr<SoupMultipartInputStream> m_multipartInputStream;
     147    GRefPtr<GCancellable> m_cancellable;
     148    GRefPtr<GAsyncResult> m_deferredResult;
     149    RunLoop::Timer<ResourceHandle> m_timeoutSource;
     150    GUniquePtr<SoupBuffer> m_soupBuffer;
     151    unsigned long m_bodySize { 0 };
     152    unsigned long m_bodyDataSent { 0 };
     153    SoupSession* soupSession();
     154    int m_redirectCount { 0 };
     155    size_t m_previousPosition { 0 };
     156    bool m_useAuthenticationManager { true };
     157    struct {
     158        Credential credential;
     159        ProtectionSpace protectionSpace;
     160    } m_credentialDataToSaveInPersistentStore;
     161#endif
     162
    128163#if PLATFORM(COCOA)
    129164    // We need to keep a reference to the original challenge to be able to cancel it.
  • trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp

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