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