ResourceHandleCFURLConnectionDelegateWithOperationQueue.cpp [plain text]
#include "config.h"
#include "ResourceHandleCFURLConnectionDelegateWithOperationQueue.h"
#if USE(CFNETWORK)
#include "AuthenticationCF.h"
#include "AuthenticationChallenge.h"
#include "CFNetworkSPI.h"
#include "Logging.h"
#include "ResourceHandle.h"
#include "ResourceHandleClient.h"
#include "ResourceResponse.h"
#include "SharedBuffer.h"
#include "WebCoreSystemInterface.h"
#include "WebCoreURLResponse.h"
#include <wtf/MainThread.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
ResourceHandleCFURLConnectionDelegateWithOperationQueue::ResourceHandleCFURLConnectionDelegateWithOperationQueue(ResourceHandle* handle)
: ResourceHandleCFURLConnectionDelegate(handle)
, m_queue(dispatch_queue_create("com.apple.WebCore/CFNetwork", DISPATCH_QUEUE_SERIAL))
, m_semaphore(dispatch_semaphore_create(0))
{
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(m_queue, backgroundQueue);
}
ResourceHandleCFURLConnectionDelegateWithOperationQueue::~ResourceHandleCFURLConnectionDelegateWithOperationQueue()
{
dispatch_release(m_semaphore);
dispatch_release(m_queue);
}
bool ResourceHandleCFURLConnectionDelegateWithOperationQueue::hasHandle() const
{
return !!m_handle;
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::releaseHandle()
{
ResourceHandleCFURLConnectionDelegate::releaseHandle();
m_requestResult = nullptr;
m_cachedResponseResult = nullptr;
m_boolResult = false;
dispatch_semaphore_signal(m_semaphore);
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupRequest(CFMutableURLRequestRef request)
{
#if PLATFORM(IOS)
CFURLRequestSetShouldStartSynchronously(request, 1);
#endif
CFURLRef requestURL = CFURLRequestGetURL(request);
if (!requestURL)
return;
m_originalScheme = adoptCF(CFURLCopyScheme(requestURL));
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupConnectionScheduling(CFURLConnectionRef connection)
{
CFURLConnectionSetDelegateDispatchQueue(connection, m_queue);
}
CFURLRequestRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(CFURLRequestRef cfRequest, CFURLResponseRef originalRedirectResponse)
{
if (!originalRedirectResponse) {
RetainPtr<CFStringRef> newScheme = adoptCF(CFURLCopyScheme(CFURLRequestGetURL(cfRequest)));
if (CFStringCompare(newScheme.get(), m_originalScheme.get(), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
CFRetain(cfRequest);
return cfRequest;
}
}
ASSERT(!isMainThread());
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
dispatch_async(dispatch_get_main_queue(), ^{
if (!protectedThis->hasHandle()) {
continueWillSendRequest(nullptr);
return;
}
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
RetainPtr<CFURLResponseRef> redirectResponse = synthesizeRedirectResponseIfNecessary(cfRequest, originalRedirectResponse);
ASSERT(redirectResponse);
ResourceRequest request = createResourceRequest(cfRequest, redirectResponse.get());
m_handle->willSendRequest(WTFMove(request), redirectResponse.get());
});
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
return m_requestResult.leakRef();
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(CFURLConnectionRef connection, CFURLResponseRef cfResponse)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
dispatch_async(dispatch_get_main_queue(), ^{
if (!protectedThis->hasHandle() || !m_handle->client()) {
continueDidReceiveResponse();
return;
}
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
auto msg = CFURLResponseGetHTTPResponse(cfResponse);
int statusCode = msg ? CFHTTPMessageGetResponseStatusCode(msg) : 0;
if (statusCode != 304) {
bool isMainResourceLoad = m_handle->firstRequest().requester() == ResourceRequest::Requester::Main;
adjustMIMETypeIfNecessary(cfResponse, isMainResourceLoad);
}
#if !PLATFORM(IOS)
if (_CFURLRequestCopyProtocolPropertyForKey(m_handle->firstRequest().cfURLRequest(DoNotUpdateHTTPBody), CFSTR("ForceHTMLMIMEType")))
CFURLResponseSetMIMEType(cfResponse, CFSTR("text/html"));
#endif // !PLATFORM(IOS)
ResourceResponse resourceResponse(cfResponse);
#if ENABLE(WEB_TIMING)
ResourceHandle::getConnectionTimingData(connection, resourceResponse.resourceLoadTiming());
#else
UNUSED_PARAM(connection);
#endif
m_handle->client()->didReceiveResponseAsync(m_handle, WTFMove(resourceResponse));
});
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(CFDataRef data, CFIndex originalLength)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
CFRetain(data);
dispatch_async(dispatch_get_main_queue(), ^{
if (protectedThis->hasHandle() && m_handle->client()) {
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFData(data), originalLength);
}
CFRelease(data);
});
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading()
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
dispatch_async(dispatch_get_main_queue(), ^{
if (!protectedThis->hasHandle() || !m_handle->client())
return;
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
m_handle->client()->didFinishLoading(m_handle, 0);
});
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(CFErrorRef error)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
CFRetain(error);
dispatch_async(dispatch_get_main_queue(), ^{
if (protectedThis->hasHandle() && m_handle->client()) {
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
m_handle->client()->didFail(m_handle, ResourceError(error));
}
CFRelease(error);
});
}
CFCachedURLResponseRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(CFCachedURLResponseRef cachedResponse)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
dispatch_async(dispatch_get_main_queue(), ^{
if (!protectedThis->hasHandle() || !m_handle->client()) {
continueWillCacheResponse(nullptr);
return;
}
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse);
});
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
return m_cachedResponseResult.leakRef();
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(CFURLAuthChallengeRef challenge)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
CFRetain(challenge);
dispatch_async(dispatch_get_main_queue(), ^{
if (protectedThis->hasHandle()) {
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
m_handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, m_handle));
}
CFRelease(challenge);
});
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
dispatch_async(dispatch_get_main_queue(), ^{
if (!protectedThis->hasHandle() || !m_handle->client())
return;
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
});
}
Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::shouldUseCredentialStorage()
{
return NO;
}
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(CFURLProtectionSpaceRef protectionSpace)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
dispatch_async(dispatch_get_main_queue(), ^{
if (!protectedThis->hasHandle()) {
continueCanAuthenticateAgainstProtectionSpace(false);
return;
}
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
ProtectionSpace coreProtectionSpace = ProtectionSpace(protectionSpace);
#if PLATFORM(IOS)
if (coreProtectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeUnknown) {
m_boolResult = false;
dispatch_semaphore_signal(m_semaphore);
return;
}
#endif // PLATFORM(IOS)
m_handle->canAuthenticateAgainstProtectionSpace(coreProtectionSpace);
});
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
return m_boolResult;
}
#endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveDataArray(CFArrayRef dataArray)
{
RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis(this);
CFRetain(dataArray);
dispatch_async(dispatch_get_main_queue(), ^{
if (protectedThis->hasHandle() && m_handle->client()) {
LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFDataArray(dataArray), -1);
}
CFRelease(dataArray);
});
}
#endif // USE(NETWORK_CFDATA_ARRAY_CALLBACK)
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillSendRequest(CFURLRequestRef request)
{
m_requestResult = request;
dispatch_semaphore_signal(m_semaphore);
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueDidReceiveResponse()
{
dispatch_semaphore_signal(m_semaphore);
}
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillCacheResponse(CFCachedURLResponseRef response)
{
m_cachedResponseResult = response;
dispatch_semaphore_signal(m_semaphore);
}
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
{
m_boolResult = canAuthenticate;
dispatch_semaphore_signal(m_semaphore);
}
#endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
}
#endif // USE(CFNETWORK)