Changeset 220939 in webkit
- Timestamp:
- Aug 18, 2017 4:02:51 PM (7 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 1 added
- 10 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r220937 r220939 1 2017-08-18 Daewoong Jang <daewoong.jang@navercorp.com> 2 3 [Curl] Improve multi-threaded networking 4 https://bugs.webkit.org/show_bug.cgi?id=175713 5 6 Reviewed by Alex Christensen. 7 8 * platform/Curl.cmake: 9 * platform/network/ResourceHandleInternal.h: 10 (WebCore::ResourceHandleInternal::ResourceHandleInternal): 11 (WebCore::ResourceHandleInternal::m_failureTimer): Deleted. 12 * platform/network/curl/CurlCacheManager.cpp: 13 (WebCore::CurlCacheManager::didReceiveResponse): 14 * platform/network/curl/CurlDownload.cpp: 15 (WebCore::CurlDownload::init): 16 (WebCore::CurlDownload::start): 17 (WebCore::CurlDownload::cancel): 18 (WebCore::CurlDownload::retain): 19 (WebCore::CurlDownload::release): 20 (WebCore::CurlDownload::setupRequest): 21 (WebCore::CurlDownload::notifyFinish): 22 (WebCore::CurlDownload::notifyFail): 23 * platform/network/curl/CurlDownload.h: 24 * platform/network/curl/CurlJobManager.cpp: 25 (WebCore::CurlJobList::isEmpty const): 26 (WebCore::CurlJobList::startJobs): 27 (WebCore::CurlJobList::finishJobs): 28 (WebCore::CurlJobList::notifyResult): 29 (WebCore::CurlJobManager::add): 30 (WebCore::CurlJobManager::cancel): 31 (WebCore::CurlJobManager::callOnJobThread): 32 (WebCore::CurlJobManager::startThreadIfNeeded): 33 (WebCore::CurlJobManager::updateJobList): 34 (WebCore::CurlJobManager::workerThread): 35 (WebCore::CurlJobList::append): Deleted. 36 (WebCore::CurlJobList::cancel): Deleted. 37 (WebCore::CurlJobList::complete): Deleted. 38 (WebCore::CurlJobList::withJob): Deleted. 39 (WebCore::CurlJobList::withCurlHandle): Deleted. 40 (WebCore::CurlJob::invoke): Deleted. 41 (WebCore::CurlJobManager::updateJobs): Deleted. 42 * platform/network/curl/CurlJobManager.h: 43 (WebCore::CurlJob::CurlJob): Deleted. 44 (WebCore::CurlJob::~CurlJob): Deleted. 45 (WebCore::CurlJob::operator=): Deleted. 46 (WebCore::CurlJob::curlHandle const): Deleted. 47 (WebCore::CurlJob::ticket const): Deleted. 48 (WebCore::CurlJob::finished): Deleted. 49 (WebCore::CurlJob::error): Deleted. 50 (WebCore::CurlJob::cancel): Deleted. 51 (WebCore::CurlJobManager::isActiveJob const): Deleted. 52 * platform/network/curl/MultipartHandle.cpp: 53 (WebCore::MultipartHandle::didReceiveData): 54 * platform/network/curl/ResourceHandleCurl.cpp: 55 (WebCore::ResourceHandleInternal::~ResourceHandleInternal): 56 (WebCore::ResourceHandle::start): 57 (WebCore::ResourceHandle::cancel): 58 (WebCore::ResourceHandle::platformSetDefersLoading): 59 (WebCore::ResourceHandle::didReceiveAuthenticationChallenge): 60 (WebCore::ResourceHandle::receivedCredential): 61 (WebCore::ResourceHandle::receivedRequestToContinueWithoutCredential): 62 (WebCore::ResourceHandle::platformLoadResourceSynchronously): 63 (WebCore::ResourceHandleInternal::initialize): Deleted. 64 (WebCore::ResourceHandleInternal::applyAuthentication): Deleted. 65 (WebCore::getFormElementsCount): Deleted. 66 (WebCore::ResourceHandleInternal::setupPUT): Deleted. 67 (WebCore::ResourceHandleInternal::setupPOST): Deleted. 68 (WebCore::ResourceHandleInternal::setupFormData): Deleted. 69 (WebCore::ResourceHandleInternal::didFinish): Deleted. 70 (WebCore::ResourceHandleInternal::didFail): Deleted. 71 (WebCore::ResourceHandleInternal::calculateWebTimingInformations): Deleted. 72 (WebCore::ResourceHandleInternal::handleLocalReceiveResponse): Deleted. 73 (WebCore::isHttpInfo): Deleted. 74 (WebCore::isHttpRedirect): Deleted. 75 (WebCore::isHttpAuthentication): Deleted. 76 (WebCore::isHttpNotModified): Deleted. 77 (WebCore::isAppendableHeader): Deleted. 78 (WebCore::removeLeadingAndTrailingQuotes): Deleted. 79 (WebCore::getProtectionSpace): Deleted. 80 (WebCore::ResourceHandleInternal::willPrepareSendData): Deleted. 81 (WebCore::ResourceHandleInternal::didReceiveHeaderLine): Deleted. 82 (WebCore::ResourceHandleInternal::didReceiveAllHeaders): Deleted. 83 (WebCore::ResourceHandleInternal::didReceiveContentData): Deleted. 84 (WebCore::ResourceHandleInternal::readCallback): Deleted. 85 (WebCore::ResourceHandleInternal::headerCallback): Deleted. 86 (WebCore::ResourceHandleInternal::writeCallback): Deleted. 87 (WebCore::ResourceHandleInternal::dispatchSynchronousJob): Deleted. 88 (WebCore::ResourceHandleInternal::handleDataURL): Deleted. 89 * platform/network/curl/ResourceHandleCurlDelegate.cpp: Added. 90 (WebCore::ResourceHandleCurlDelegate::ResourceHandleCurlDelegate): 91 (WebCore::ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate): 92 (WebCore::ResourceHandleCurlDelegate::hasHandle const): 93 (WebCore::ResourceHandleCurlDelegate::releaseHandle): 94 (WebCore::ResourceHandleCurlDelegate::start): 95 (WebCore::ResourceHandleCurlDelegate::cancel): 96 (WebCore::ResourceHandleCurlDelegate::setDefersLoading): 97 (WebCore::ResourceHandleCurlDelegate::setAuthentication): 98 (WebCore::ResourceHandleCurlDelegate::dispatchSynchronousJob): 99 (WebCore::ResourceHandleCurlDelegate::retain): 100 (WebCore::ResourceHandleCurlDelegate::release): 101 (WebCore::ResourceHandleCurlDelegate::setupRequest): 102 (WebCore::ResourceHandleCurlDelegate::notifyFinish): 103 (WebCore::ResourceHandleCurlDelegate::notifyFail): 104 (WebCore::ResourceHandleCurlDelegate::response): 105 (WebCore::ResourceHandleCurlDelegate::setupAuthentication): 106 (WebCore::removeLeadingAndTrailingQuotes): 107 (WebCore::ResourceHandleCurlDelegate::getProtectionSpace): 108 (WebCore::isHttpInfo): 109 (WebCore::isHttpRedirect): 110 (WebCore::isHttpAuthentication): 111 (WebCore::isHttpNotModified): 112 (WebCore::isAppendableHeader): 113 (WebCore::ResourceHandleCurlDelegate::didReceiveHeaderLine): 114 (WebCore::ResourceHandleCurlDelegate::didReceiveAllHeaders): 115 (WebCore::ResourceHandleCurlDelegate::didReceiveContentData): 116 (WebCore::ResourceHandleCurlDelegate::handleLocalReceiveResponse): 117 (WebCore::ResourceHandleCurlDelegate::prepareSendData): 118 (WebCore::ResourceHandleCurlDelegate::didFinish): 119 (WebCore::ResourceHandleCurlDelegate::didFail): 120 (WebCore::ResourceHandleCurlDelegate::handleDataURL): 121 (WebCore::ResourceHandleCurlDelegate::setupPOST): 122 (WebCore::ResourceHandleCurlDelegate::setupPUT): 123 (WebCore::ResourceHandleCurlDelegate::getFormElementsCount): 124 (WebCore::ResourceHandleCurlDelegate::setupFormData): 125 (WebCore::ResourceHandleCurlDelegate::applyAuthentication): 126 (WebCore::ResourceHandleCurlDelegate::setWebTimings): 127 (WebCore::ResourceHandleCurlDelegate::didReceiveHeader): 128 (WebCore::ResourceHandleCurlDelegate::didReceiveData): 129 (WebCore::ResourceHandleCurlDelegate::willSendData): 130 (WebCore::ResourceHandleCurlDelegate::didReceiveHeaderCallback): 131 (WebCore::ResourceHandleCurlDelegate::didReceiveDataCallback): 132 (WebCore::ResourceHandleCurlDelegate::willSendDataCallback): 133 * platform/network/curl/ResourceHandleCurlDelegate.h: Added. 134 1 135 2017-08-18 Ryosuke Niwa <rniwa@webkit.org> 2 136 -
trunk/Source/WebCore/platform/Curl.cmake
r219606 r220939 16 16 platform/network/curl/ProxyServerCurl.cpp 17 17 platform/network/curl/ResourceHandleCurl.cpp 18 platform/network/curl/ResourceHandleCurlDelegate.cpp 18 19 platform/network/curl/SSLHandle.cpp 19 20 platform/network/curl/SocketStreamHandleImplCurl.cpp -
trunk/Source/WebCore/platform/network/ResourceHandleInternal.h
r219954 r220939 44 44 45 45 #if USE(CURL) 46 #include "CurlContext.h" 47 #include "CurlJobManager.h" 48 #include "FormDataStreamCurl.h" 49 #include "MultipartHandle.h" 50 #include <wtf/Lock.h> 46 #include "ResourceHandleCurlDelegate.h" 51 47 #endif 52 48 … … 88 84 #if USE(CFURLCONNECTION) 89 85 , m_currentRequest(request) 90 #endif91 #if USE(CURL)92 , m_handle { loader }93 , m_formDataStream { loader }94 86 #endif 95 87 #if USE(SOUP) … … 141 133 #endif 142 134 #if USE(CURL) 143 ResourceHandle* m_handle; 144 CurlHandle m_curlHandle; 145 135 RefPtr<ResourceHandleCurlDelegate> m_delegate; 146 136 ResourceResponse m_response; 147 bool m_cancelled { false };148 unsigned short m_authFailureCount { 0 };149 150 FormDataStream m_formDataStream;151 unsigned m_sslErrors { 0 };152 Vector<char> m_postBytes;153 154 std::unique_ptr<MultipartHandle> m_multipartHandle;155 bool m_addedCacheValidationHeaders { false };156 CurlJobTicket m_job { nullptr };157 158 Vector<char> m_receivedBuffer;159 Lock m_receivedBufferMutex;160 161 void initialize();162 void applyAuthentication();163 void setupPOST();164 void setupPUT();165 void setupFormData(bool isPostRequest);166 167 void didFinish();168 void didFail();169 170 size_t willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks);171 void didReceiveHeaderLine(const String& header);172 void didReceiveAllHeaders(long httpCode, long long contentLength);173 void didReceiveContentData();174 175 void handleLocalReceiveResponse();176 177 static size_t readCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);178 static size_t headerCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);179 static size_t writeCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);180 181 void dispatchSynchronousJob();182 void handleDataURL();183 184 void calculateWebTimingInformations();185 186 137 #endif 187 138 -
trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp
r194496 r220939 200 200 201 201 ResourceHandleInternal* d = job.getInternal(); 202 if ( d->m_cancelled)202 if (!d->m_delegate->hasHandle()) 203 203 return; 204 204 -
trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp
r220897 r220939 59 59 LockHolder locker(m_mutex); 60 60 61 setupRequest();62 63 61 m_listener = listener; 64 62 m_url = url; … … 79 77 bool CurlDownload::start() 80 78 { 81 m_job = CurlJobManager::singleton().add(m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) mutable { 82 switch (result) { 83 case CurlJobResult::Done: 84 didFinish(); 85 break; 86 87 case CurlJobResult::Error: 88 didFail(); 89 break; 90 91 case CurlJobResult::Cancelled: 92 break; 93 } 94 }); 95 return true; 79 m_job = CurlJobManager::singleton().add(m_curlHandle, *this); 80 return !!m_job; 96 81 } 97 82 98 83 bool CurlDownload::cancel() 99 84 { 100 CurlJobManager::singleton().cancel(m_job); 85 CurlJobTicket job = m_job; 86 m_job = nullptr; 87 CurlJobManager::singleton().cancel(job); 101 88 return true; 102 89 } … … 108 95 response.setHTTPHeaderField(m_httpHeaderFieldName, m_httpHeaderFieldValue); 109 96 return response; 97 } 98 99 void CurlDownload::retain() 100 { 101 ref(); 102 } 103 104 void CurlDownload::release() 105 { 106 deref(); 110 107 } 111 108 … … 124 121 m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY); 125 122 m_curlHandle.enableCAInfoIfExists(); 123 } 124 125 void CurlDownload::notifyFinish() 126 { 127 callOnMainThread([protectedThis = makeRef(*this)] { 128 if (!protectedThis->m_job) 129 return; 130 protectedThis->didFinish(); 131 }); 132 } 133 134 void CurlDownload::notifyFail() 135 { 136 callOnMainThread([protectedThis = makeRef(*this)] { 137 if (!protectedThis->m_job) 138 return; 139 protectedThis->didFail(); 140 }); 126 141 } 127 142 -
trunk/Source/WebCore/platform/network/curl/CurlDownload.h
r220897 r220939 45 45 }; 46 46 47 class CurlDownload : public ThreadSafeRefCounted<CurlDownload> {47 class CurlDownload : public ThreadSafeRefCounted<CurlDownload>, public CurlJobClient { 48 48 public: 49 49 CurlDownload(); … … 66 66 67 67 private: 68 void setupRequest(); 68 void retain() override; 69 void release() override; 70 71 void setupRequest() override; 72 void notifyFinish() override; 73 void notifyFail() override; 69 74 70 75 void closeFile(); -
trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp
r219606 r220939 2 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 3 * Copyright (C) 2017 Sony Interactive Entertainment Inc. 4 * Copyright (C) 2017 NAVER Corp. 4 5 * 5 6 * Redistribution and use in source and binary forms, with or without … … 26 27 27 28 #include "config.h" 29 #include "CurlJobManager.h" 28 30 29 31 #if USE(CURL) 30 #include "CurlJobManager.h" 31 32 33 #include "ResourceHandleCurlDelegate.h" 32 34 #include <iterator> 33 35 #include <wtf/MainThread.h> … … 37 39 38 40 namespace WebCore { 41 42 enum class CurlJobResult { Done, Error, Cancelled }; 39 43 40 44 /* … … 42 46 */ 43 47 class CurlJobList : public CurlMultiHandle { 44 using Predicate = WTF::Function<bool(const CurlJob&)>;45 using Action = WTF::Function<void(CurlJob&)>;46 using JobMap = HashMap<CurlJobTicket, CurlJob>;47 48 48 public: 49 void append(CurlJob& job) 50 { 51 CurlHandle* curl = job.curlHandle(); 52 53 CURLMcode retval = addHandle(curl->handle()); 54 // @FIXME error logging 55 if (retval == CURLM_OK) 56 m_jobs.set(job.ticket(), WTFMove(job)); 57 } 58 59 void cancel(CurlJobTicket ticket) 60 { 61 complete(ticket, [](auto&& job) { job.cancel(); }); 62 } 63 64 void complete(CurlJobTicket ticket, Action action) 65 { 66 auto found = m_jobs.find(ticket); 67 if (found != m_jobs.end()) { 68 auto job = WTFMove(found->value); 69 70 removeHandle(job.curlHandle()->handle()); 71 action(job); 72 73 m_jobs.remove(found); 74 } 75 } 76 77 bool isEmpty() const { return m_jobs.isEmpty(); } 78 79 bool withJob(CurlJobTicket ticket, WTF::Function<void(JobMap::iterator)> callback) 80 { 81 auto found = m_jobs.find(ticket); 82 if (found == m_jobs.end()) 83 return false; 84 85 callback(found); 86 return true; 87 } 88 89 bool withCurlHandle(CurlJobTicket ticket, WTF::Function<void(CurlHandle&)> callback) 90 { 91 return withJob(ticket, [&callback](JobMap::iterator it) { 92 callback(*it->value.curlHandle()); 93 }); 49 bool isEmpty() const { return m_activeJobs.isEmpty(); } 50 51 void startJobs(HashMap<CurlJobTicket, CurlJobClient*>&& jobs) 52 { 53 auto localJobs = WTFMove(jobs); 54 for (auto& job : localJobs) { 55 job.value->setupRequest(); 56 m_activeJobs.add(job.key, job.value); 57 addHandle(job.key); 58 } 59 } 60 61 void finishJobs(HashSet<CurlJobTicket>&& tickets, CurlJobResult result) 62 { 63 auto localTickets = WTFMove(tickets); 64 for (auto& ticket : localTickets) { 65 if (!m_activeJobs.contains(ticket)) 66 continue; 67 removeHandle(ticket); 68 notifyResult(m_activeJobs.fastGet(ticket), result); 69 m_activeJobs.remove(ticket); 70 } 94 71 } 95 72 96 73 private: 97 JobMap m_jobs; 74 void notifyResult(CurlJobClient* client, CurlJobResult result) 75 { 76 switch (result) { 77 case CurlJobResult::Done: 78 client->notifyFinish(); 79 break; 80 case CurlJobResult::Error: 81 client->notifyFail(); 82 break; 83 case CurlJobResult::Cancelled: 84 break; 85 } 86 87 client->release(); 88 } 89 90 HashMap<CurlJobTicket, CurlJobClient*> m_activeJobs; 98 91 }; 99 92 100 void CurlJob::invoke(CurlJobResult result) 101 { 102 callOnMainThread([job = WTFMove(m_job), result] { 103 job(result); 104 }); 105 } 106 107 CurlJobTicket CurlJobManager::add(CurlHandle& curl, Callback callback) 93 CurlJobTicket CurlJobManager::add(CurlHandle& curl, CurlJobClient& client) 108 94 { 109 95 ASSERT(isMainThread()); 110 96 97 client.retain(); 98 111 99 CurlJobTicket ticket = static_cast<CurlJobTicket>(curl.handle()); 112 100 113 101 { 114 102 LockHolder locker(m_mutex); 115 116 if (isActiveJob(ticket)) 117 return ticket; 118 119 120 m_pendingJobs.append(CurlJob { &curl, WTFMove(callback) }); 121 m_activeJobs.add(ticket); 103 m_cancelledTickets.remove(ticket); 104 m_pendingJobs.add(ticket, &client); 122 105 } 123 106 … … 127 110 } 128 111 129 boolCurlJobManager::cancel(CurlJobTicket job)112 void CurlJobManager::cancel(CurlJobTicket job) 130 113 { 131 114 ASSERT(isMainThread()); 132 115 133 if (m_runThread) { 134 LockHolder locker(m_mutex); 135 136 if (!isActiveJob(job)) 137 return false; 138 139 m_cancelledTickets.append(job); 140 m_activeJobs.remove(job); 141 } 142 143 return true; 144 } 145 146 void CurlJobManager::callOnJobThread(WTF::Function<void()>&& callback) 147 { 148 LockHolder locker { m_mutex }; 149 150 m_taskQueue.append(std::make_unique<WTF::Function<void()>>(WTFMove(callback))); 116 LockHolder locker(m_mutex); 117 m_cancelledTickets.add(job); 118 } 119 120 void CurlJobManager::callOnJobThread(WTF::Function<void()>&& task) 121 { 122 LockHolder locker(m_mutex); 123 m_taskQueue.append(WTFMove(task)); 151 124 } 152 125 … … 163 136 m_thread = Thread::create("curlThread", [this] { 164 137 workerThread(); 138 m_runThread = false; 165 139 }); 166 140 } … … 187 161 } 188 162 189 bool CurlJobManager::updateJobs(CurlJobList& jobs)163 void CurlJobManager::updateJobList(CurlJobList& jobs) 190 164 { 191 165 ASSERT(!isMainThread()); 192 166 193 Vector<CurlJob> pendingJobs; 194 Vector<CurlJobTicket> cancelledTickets; 167 HashMap<CurlJobTicket, CurlJobClient*> pendingJobs; 168 HashSet<CurlJobTicket> cancelledTickets; 169 Vector<WTF::Function<void()>> taskQueue; 170 195 171 { 196 172 LockHolder locker(m_mutex); 197 if (!m_runThread)198 return false;199 173 200 174 pendingJobs = WTFMove(m_pendingJobs); 201 175 cancelledTickets = WTFMove(m_cancelledTickets); 202 } 203 204 for (auto& job : pendingJobs) 205 jobs.append(job); 206 207 for (auto& ticket : cancelledTickets) 208 jobs.cancel(ticket); 209 210 for (auto& callback : m_taskQueue.takeAllMessages()) { 211 (*callback)(); 212 } 213 214 return true; 176 taskQueue = WTFMove(m_taskQueue); 177 } 178 179 jobs.startJobs(WTFMove(pendingJobs)); 180 jobs.finishJobs(WTFMove(cancelledTickets), CurlJobResult::Cancelled); 181 jobs.finishJobs(WTFMove(m_finishedTickets), CurlJobResult::Done); 182 jobs.finishJobs(WTFMove(m_failedTickets), CurlJobResult::Error); 183 184 for (auto& task : taskQueue) 185 task(); 215 186 } 216 187 … … 221 192 CurlJobList jobs; 222 193 223 while (updateJobs(jobs)) { 194 while (m_runThread) { 195 updateJobList(jobs); 196 224 197 // Retry 'select' if it was interrupted by a process signal. 225 198 int rc = 0; … … 256 229 break; 257 230 258 if (msg->msg == CURLMSG_DONE) { 259 auto ticket = static_cast<CurlJobTicket>(msg->easy_handle); 260 CURLcode result = msg->data.result; 261 262 { 263 LockHolder locker(m_mutex); 264 m_activeJobs.remove(ticket); 265 } 266 267 jobs.complete(ticket, [result](auto& job) { 268 job.curlHandle()->setErrorCode(result); 269 270 bool done = result == CURLE_OK; 271 if (done) 272 job.finished(); 273 else { 274 URL url = job.curlHandle()->getEffectiveURL(); 275 #ifndef NDEBUG 276 fprintf(stderr, "Curl ERROR for url='%s', error: '%s'\n", url.string().utf8().data(), job.curlHandle()->errorDescription().utf8().data()); 231 ASSERT(msg->msg == CURLMSG_DONE); 232 auto ticket = static_cast<CurlJobTicket>(msg->easy_handle); 233 if (msg->data.result == CURLE_OK) 234 m_finishedTickets.add(ticket); 235 else 236 m_failedTickets.add(ticket); 237 } 238 239 if (jobs.isEmpty()) 240 stopThreadIfNoMoreJobRunning(); 241 } 242 } 243 244 } 245 277 246 #endif 278 job.error();279 }280 });281 } else282 ASSERT_NOT_REACHED();283 }284 285 if (jobs.isEmpty()) {286 stopThreadIfNoMoreJobRunning();287 if (!m_runThread)288 break;289 }290 }291 }292 293 }294 295 #endif -
trunk/Source/WebCore/platform/network/curl/CurlJobManager.h
r219606 r220939 2 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 3 * Copyright (C) 2017 Sony Interactive Entertainment Inc. 4 * Copyright (C) 2017 NAVER Corp. 4 5 * 5 6 * Redistribution and use in source and binary forms, with or without … … 28 29 29 30 #include "CurlContext.h" 30 31 #include <wtf/Function.h>32 31 #include <wtf/HashMap.h> 33 32 #include <wtf/HashSet.h> 34 33 #include <wtf/Lock.h> 35 #include <wtf/MessageQueue.h>36 34 #include <wtf/Noncopyable.h> 37 35 #include <wtf/Threading.h> 38 #include <wtf/Vector.h>39 40 #if OS(WINDOWS)41 #include <windows.h>42 #include <winsock2.h>43 #endif44 45 36 46 37 namespace WebCore { 47 38 48 enum class CurlJobResult { Done, Error, Cancelled };39 class CurlJobList; 49 40 using CurlJobTicket = void*; 50 using CurlJobCallback = WTF::Function<void(CurlJobResult)>;51 using CurlJobTask = WTF::Function<void(CurlHandle&)>;52 41 53 class CurlJobList; 42 class CurlJobClient { 43 public: 44 virtual void retain() = 0; 45 virtual void release() = 0; 54 46 55 class CurlJob { 56 WTF_MAKE_NONCOPYABLE(CurlJob); 57 public: 58 CurlJob() { } 59 CurlJob(CurlHandle* curl, CurlJobCallback job) 60 : m_curl { curl }, m_job { WTFMove(job) } { } 61 CurlJob(CurlJob&& other) 62 { 63 m_curl = other.m_curl; 64 other.m_curl = nullptr; 65 m_job = WTFMove(other.m_job); 66 } 67 ~CurlJob() { } 68 69 CurlJob& operator=(CurlJob&& other) 70 { 71 m_curl = other.m_curl; 72 other.m_curl = nullptr; 73 m_job = WTFMove(other.m_job); 74 return *this; 75 } 76 77 CurlHandle* curlHandle() const { return m_curl; } 78 CurlJobTicket ticket() const { return static_cast<CurlJobTicket>(m_curl->handle()); } 79 80 void finished() { invoke(CurlJobResult::Done); } 81 void error() { invoke(CurlJobResult::Error); } 82 void cancel() { invoke(CurlJobResult::Cancelled); } 83 84 private: 85 CurlHandle* m_curl; 86 CurlJobCallback m_job; 87 88 void invoke(CurlJobResult); 47 virtual void setupRequest() = 0; 48 virtual void notifyFinish() = 0; 49 virtual void notifyFail() = 0; 89 50 }; 90 91 51 92 52 class CurlJobManager { 93 53 WTF_MAKE_NONCOPYABLE(CurlJobManager); 94 using Callback = CurlJobCallback;95 96 54 public: 97 98 55 static CurlJobManager& singleton() 99 56 { … … 105 62 ~CurlJobManager() { stopThread(); } 106 63 107 CurlJobTicket add(CurlHandle&, Callback); 108 bool cancel(CurlJobTicket); 64 CurlJobTicket add(CurlHandle&, CurlJobClient&); 65 void cancel(CurlJobTicket); 66 109 67 void callOnJobThread(WTF::Function<void()>&&); 110 68 … … 114 72 void stopThread(); 115 73 116 bool updateJobs(CurlJobList& jobs); 117 bool isActiveJob(CurlJobTicket job) const { return m_activeJobs.contains(job); } 74 void updateJobList(CurlJobList&); 118 75 119 76 void workerThread(); 120 77 121 78 RefPtr<Thread> m_thread; 122 Vector<CurlJob> m_pendingJobs; 123 HashSet<CurlJobTicket> m_activeJobs; 124 Vector<CurlJobTicket> m_cancelledTickets; 125 MessageQueue<WTF::Function<void()>> m_taskQueue; 79 HashMap<CurlJobTicket, CurlJobClient*> m_pendingJobs; 80 HashSet<CurlJobTicket> m_cancelledTickets; 81 HashSet<CurlJobTicket> m_finishedTickets; 82 HashSet<CurlJobTicket> m_failedTickets; 83 Vector<WTF::Function<void()>> m_taskQueue; 126 84 mutable Lock m_mutex; 127 bool m_runThread { };85 bool m_runThread { false }; 128 86 }; 129 87 -
trunk/Source/WebCore/platform/network/curl/MultipartHandle.cpp
r201954 r220939 322 322 ResourceHandleInternal* d = m_resourceHandle->getInternal(); 323 323 324 if ( d->m_cancelled) {324 if (!d->m_delegate->hasHandle()) { 325 325 // Request has been canceled, so we'll go to the end state. 326 326 m_state = End; -
trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp
r220897 r220939 4 4 * Copyright (C) 2017 Sony Interactive Entertainment Inc. 5 5 * All rights reserved. 6 * Copyright (C) 2017 NAVER Corp. 6 7 * 7 8 * Redistribution and use in source and binary forms, with or without … … 50 51 ResourceHandleInternal::~ResourceHandleInternal() 51 52 { 53 if (m_delegate) 54 m_delegate->releaseHandle(); 52 55 } 53 56 … … 59 62 { 60 63 ASSERT(isMainThread()); 64 65 CurlContext::singleton(); 61 66 62 67 // The frame could be null if the ResourceHandle is not associated to any … … 68 73 return false; 69 74 70 d->initialize(); 71 72 d->m_job = CurlJobManager::singleton().add(d->m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) { 73 ASSERT(isMainThread()); 74 75 switch (result) { 76 case CurlJobResult::Done: 77 d->didFinish(); 78 break; 79 80 case CurlJobResult::Error: 81 d->didFail(); 82 break; 83 84 case CurlJobResult::Cancelled: 85 break; 86 } 87 }); 88 ASSERT(d->m_job); 89 90 return true; 75 d->m_delegate = adoptRef(new ResourceHandleCurlDelegate(this)); 76 return d->m_delegate->start(); 91 77 } 92 78 93 79 void ResourceHandle::cancel() 94 80 { 95 d->m_cancelled = true; 96 CurlJobManager::singleton().cancel(d->m_job); 97 } 98 99 void ResourceHandleInternal::initialize() 100 { 101 CurlContext& context = CurlContext::singleton(); 102 103 URL url = m_firstRequest.url(); 104 105 // Remove any fragment part, otherwise curl will send it as part of the request. 106 url.removeFragmentIdentifier(); 107 108 String urlString = url.string(); 109 110 if (url.isLocalFile()) { 111 // Remove any query part sent to a local file. 112 if (!url.query().isEmpty()) { 113 // By setting the query to a null string it'll be removed. 114 url.setQuery(String()); 115 urlString = url.string(); 116 } 117 // Determine the MIME type based on the path. 118 m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url)); 119 } 120 121 if (m_defersLoading) { 122 CURLcode error = m_curlHandle.pause(CURLPAUSE_ALL); 123 // If we did not pause the handle, we would ASSERT in the 124 // header callback. So just assert here. 125 ASSERT_UNUSED(error, error == CURLE_OK); 126 } 127 128 #ifndef NDEBUG 129 m_curlHandle.enableVerboseIfUsed(); 130 m_curlHandle.enableStdErrIfUsed(); 131 #endif 132 133 m_curlHandle.initialize(); 134 m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable); 135 m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck); 136 m_curlHandle.setPrivateData(this); 137 m_curlHandle.setWriteCallbackFunction(writeCallback, this); 138 m_curlHandle.setHeaderCallbackFunction(headerCallback, this); 139 m_curlHandle.enableAutoReferer(); 140 m_curlHandle.enableFollowLocation(); 141 m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY); 142 m_curlHandle.enableShareHandle(); 143 m_curlHandle.enableTimeout(); 144 m_curlHandle.enableAllowedProtocols(); 145 auto certificate = getSSLClientCertificate(m_firstRequest.url().host()); 146 if (certificate) { 147 m_curlHandle.setSslCert((*certificate).first.utf8().data()); 148 m_curlHandle.setSslCertType("P12"); 149 m_curlHandle.setSslKeyPassword((*certificate).second.utf8().data()); 150 } 151 152 if (CurlContext::singleton().shouldIgnoreSSLErrors()) 153 m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerDisable); 154 else 155 setSSLVerifyOptions(m_curlHandle); 156 157 m_curlHandle.enableCAInfoIfExists(); 158 159 m_curlHandle.enableAcceptEncoding(); 160 m_curlHandle.setUrl(urlString); 161 m_curlHandle.enableCookieJarIfExists(); 162 163 if (m_firstRequest.httpHeaderFields().size()) { 164 auto customHeaders = m_firstRequest.httpHeaderFields(); 165 auto& cache = CurlCacheManager::getInstance(); 166 167 bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch); 168 if (!hasCacheHeaders && cache.isCached(url)) { 169 cache.addCacheEntryClient(url, m_handle); 170 171 // append additional cache information 172 for (auto entry : cache.requestHeaders(url)) 173 customHeaders.set(entry.key, entry.value); 174 175 m_addedCacheValidationHeaders = true; 176 } 177 178 m_curlHandle.appendRequestHeaders(customHeaders); 179 } 180 181 String method = m_firstRequest.httpMethod(); 182 if ("GET" == method) 183 m_curlHandle.enableHttpGetRequest(); 184 else if ("POST" == method) 185 setupPOST(); 186 else if ("PUT" == method) 187 setupPUT(); 188 else if ("HEAD" == method) 189 m_curlHandle.enableHttpHeadRequest(); 190 else { 191 m_curlHandle.setHttpCustomRequest(method); 192 setupPUT(); 193 } 194 195 m_curlHandle.enableRequestHeaders(); 196 197 applyAuthentication(); 198 199 m_curlHandle.enableProxyIfExists(); 200 } 201 202 void ResourceHandleInternal::applyAuthentication() 203 { 204 // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open(). 205 String partition = m_firstRequest.cachePartition(); 206 207 if (m_handle->shouldUseCredentialStorage()) { 208 if (m_user.isEmpty() && m_pass.isEmpty()) { 209 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 210 // try and reuse the credential preemptively, as allowed by RFC 2617. 211 m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, m_firstRequest.url()); 212 } else { 213 // If there is already a protection space known for the URL, update stored credentials 214 // before sending a request. This makes it possible to implement logout by sending an 215 // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that 216 // an authentication dialog doesn't pop up). 217 CredentialStorage::defaultCredentialStorage().set(partition, Credential(m_user, m_pass, CredentialPersistenceNone), m_firstRequest.url()); 218 } 219 } 220 221 String user = m_user; 222 String password = m_pass; 223 224 if (!m_initialCredential.isEmpty()) { 225 user = m_initialCredential.user(); 226 password = m_initialCredential.password(); 227 m_curlHandle.enableHttpAuthentication(CURLAUTH_BASIC); 228 } 229 230 // It seems we need to set CURLOPT_USERPWD even if username and password is empty. 231 // Otherwise cURL will not automatically continue with a new request after a 401 response. 232 233 // curl CURLOPT_USERPWD expects username:password 234 m_curlHandle.setHttpAuthUserPass(user, password); 235 } 236 237 static inline size_t getFormElementsCount(ResourceHandle* job) 238 { 239 RefPtr<FormData> formData = job->firstRequest().httpBody(); 240 241 if (!formData) 242 return 0; 243 244 // Resolve the blob elements so the formData can correctly report it's size. 245 formData = formData->resolveBlobReferences(); 246 size_t size = formData->elements().size(); 247 job->firstRequest().setHTTPBody(WTFMove(formData)); 248 249 return size; 250 } 251 252 void ResourceHandleInternal::setupPUT() 253 { 254 m_curlHandle.enableHttpPutRequest(); 255 256 // Disable the Expect: 100 continue header 257 m_curlHandle.appendRequestHeader("Expect:"); 258 259 size_t numElements = getFormElementsCount(m_handle); 260 if (!numElements) 261 return; 262 263 setupFormData(false); 264 } 265 266 void ResourceHandleInternal::setupPOST() 267 { 268 m_curlHandle.enableHttpPostRequest(); 269 270 size_t numElements = getFormElementsCount(m_handle); 271 if (!numElements) 272 return; 273 274 // Do not stream for simple POST data 275 if (numElements == 1) { 276 m_firstRequest.httpBody()->flatten(m_postBytes); 277 if (m_postBytes.size()) 278 m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size()); 279 return; 280 } 281 282 setupFormData(true); 283 } 284 285 void ResourceHandleInternal::setupFormData(bool isPostRequest) 286 { 287 Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements(); 288 size_t numElements = elements.size(); 289 290 static const long long maxCurlOffT = m_curlHandle.maxCurlOffT(); 291 292 // Obtain the total size of the form data 293 curl_off_t size = 0; 294 bool chunkedTransfer = false; 295 for (size_t i = 0; i < numElements; i++) { 296 FormDataElement element = elements[i]; 297 if (element.m_type == FormDataElement::Type::EncodedFile) { 298 long long fileSizeResult; 299 if (getFileSize(element.m_filename, fileSizeResult)) { 300 if (fileSizeResult > maxCurlOffT) { 301 // File size is too big for specifying it to cURL 302 chunkedTransfer = true; 303 break; 304 } 305 size += fileSizeResult; 306 } else { 307 chunkedTransfer = true; 308 break; 309 } 310 } else 311 size += elements[i].m_data.size(); 312 } 313 314 // cURL guesses that we want chunked encoding as long as we specify the header 315 if (chunkedTransfer) 316 m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked"); 317 else { 318 if (isPostRequest) 319 m_curlHandle.setPostFieldLarge(size); 320 else 321 m_curlHandle.setInFileSizeLarge(size); 322 } 323 324 m_curlHandle.setReadCallbackFunction(readCallback, this); 81 d->m_delegate->cancel(); 325 82 } 326 83 … … 358 115 ASSERT(isMainThread()); 359 116 360 auto action = [defers, this, protectedThis = makeRef(*this)]() { 361 if (defers) { 362 CURLcode error = d->m_curlHandle.pause(CURLPAUSE_ALL); 363 // If we could not defer the handle, so don't do it. 364 if (error != CURLE_OK) 365 return; 366 } else { 367 CURLcode error = d->m_curlHandle.pause(CURLPAUSE_CONT); 368 if (error != CURLE_OK) { 369 // Restarting the handle has failed so just cancel it. 370 cancel(); 371 } 372 } 373 }; 374 375 if (d->m_job) { 376 CurlJobManager::singleton().callOnJobThread(WTFMove(action)); 377 } else { 378 action(); 379 } 380 } 381 382 void ResourceHandleInternal::didFinish() 383 { 384 calculateWebTimingInformations(); 385 386 if (m_cancelled) 387 return; 388 389 if (!m_response.responseFired()) { 390 handleLocalReceiveResponse(); 391 if (m_cancelled) 392 return; 393 } 394 395 if (m_multipartHandle) 396 m_multipartHandle->contentEnded(); 397 398 if (client()) { 399 client()->didFinishLoading(m_handle); 400 CurlCacheManager::getInstance().didFinishLoading(*m_handle); 401 } 402 } 403 404 void ResourceHandleInternal::didFail() 405 { 406 if (m_cancelled) 407 return; 408 URL url = m_curlHandle.getEffectiveURL(); 409 if (client()) { 410 client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors())); 411 CurlCacheManager::getInstance().didFail(*m_handle); 412 } 117 d->m_delegate->setDefersLoading(defers); 413 118 } 414 119 … … 432 137 CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore); 433 138 434 d->m_ curlHandle.setHttpAuthUserPass(credential.user(), credential.password());139 d->m_delegate->setAuthentication(credential.user(), credential.password()); 435 140 436 141 d->m_user = String(); … … 457 162 } 458 163 459 d->m_ curlHandle.setHttpAuthUserPass(credential.user(), credential.password());164 d->m_delegate->setAuthentication(credential.user(), credential.password()); 460 165 return; 461 166 } … … 490 195 } 491 196 492 d->m_ curlHandle.setHttpAuthUserPass(credential.user(), credential.password());197 d->m_delegate->setAuthentication(credential.user(), credential.password()); 493 198 clearAuthentication(); 494 199 } … … 501 206 return; 502 207 503 d->m_ curlHandle.setHttpAuthUserPass("", "");208 d->m_delegate->setAuthentication("", ""); 504 209 clearAuthentication(); 505 210 } … … 526 231 } 527 232 528 void ResourceHandleInternal::calculateWebTimingInformations()529 {530 double preTransferTime = 0;531 double dnslookupTime = 0;532 double connectTime = 0;533 double appConnectTime = 0;534 535 m_curlHandle.getTimes(preTransferTime, dnslookupTime, connectTime, appConnectTime);536 537 m_response.deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);538 m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnslookupTime);539 540 m_response.deprecatedNetworkLoadMetrics().connectStart = Seconds(dnslookupTime);541 m_response.deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);542 543 m_response.deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);544 m_response.deprecatedNetworkLoadMetrics().responseStart = Seconds(preTransferTime);545 546 if (appConnectTime)547 m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);548 }549 550 void ResourceHandleInternal::handleLocalReceiveResponse()551 {552 ASSERT(isMainThread());553 554 // since the code in headerCallback will not have run for local files555 // the code to set the URL and fire didReceiveResponse is never run,556 // which means the ResourceLoader's response does not contain the URL.557 // Run the code here for local files to resolve the issue.558 // TODO: See if there is a better approach for handling this.559 URL url = m_curlHandle.getEffectiveURL();560 ASSERT(url.isValid());561 m_response.setURL(url);562 if (client())563 client()->didReceiveResponse(m_handle, ResourceResponse(m_response));564 m_response.setResponseFired(true);565 }566 567 inline static bool isHttpInfo(int statusCode)568 {569 return 100 <= statusCode && statusCode < 200;570 }571 572 inline static bool isHttpRedirect(int statusCode)573 {574 return 300 <= statusCode && statusCode < 400 && statusCode != 304;575 }576 577 inline static bool isHttpAuthentication(int statusCode)578 {579 return statusCode == 401;580 }581 582 inline static bool isHttpNotModified(int statusCode)583 {584 return statusCode == 304;585 }586 587 static bool isAppendableHeader(const String &key)588 {589 static const char* appendableHeaders[] = {590 "access-control-allow-headers",591 "access-control-allow-methods",592 "access-control-allow-origin",593 "access-control-expose-headers",594 "allow",595 "cache-control",596 "connection",597 "content-encoding",598 "content-language",599 "if-match",600 "if-none-match",601 "keep-alive",602 "pragma",603 "proxy-authenticate",604 "public",605 "server",606 "set-cookie",607 "te",608 "trailer",609 "transfer-encoding",610 "upgrade",611 "user-agent",612 "vary",613 "via",614 "warning",615 "www-authenticate"616 };617 618 // Custom headers start with 'X-', and need no further checking.619 if (key.startsWith("x-", /* caseSensitive */ false))620 return true;621 622 for (auto& header : appendableHeaders) {623 if (equalIgnoringASCIICase(key, header))624 return true;625 }626 627 return false;628 }629 630 static void removeLeadingAndTrailingQuotes(String& value)631 {632 unsigned length = value.length();633 if (value.startsWith('"') && value.endsWith('"') && length > 1)634 value = value.substring(1, length - 2);635 }636 637 static bool getProtectionSpace(ResourceHandle* job, const ResourceResponse& response, ProtectionSpace& protectionSpace)638 {639 ResourceHandleInternal* d = job->getInternal();640 641 CURLcode err;642 643 long port = 0;644 err = d->m_curlHandle.getPrimaryPort(port);645 if (err != CURLE_OK)646 return false;647 648 long availableAuth = CURLAUTH_NONE;649 err = d->m_curlHandle.getHttpAuthAvail(availableAuth);650 if (err != CURLE_OK)651 return false;652 653 URL url = d->m_curlHandle.getEffectiveURL();654 if (!url.isValid())655 return false;656 657 String host = url.host();658 StringView protocol = url.protocol();659 660 String realm;661 662 const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);663 const String realmString = "realm=";664 int realmPos = authHeader.find(realmString);665 if (realmPos > 0) {666 realm = authHeader.substring(realmPos + realmString.length());667 realm = realm.left(realm.find(','));668 removeLeadingAndTrailingQuotes(realm);669 }670 671 ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;672 if (protocol == "https")673 serverType = ProtectionSpaceServerHTTPS;674 675 ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;676 677 if (availableAuth & CURLAUTH_BASIC)678 authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;679 if (availableAuth & CURLAUTH_DIGEST)680 authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;681 if (availableAuth & CURLAUTH_GSSNEGOTIATE)682 authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;683 if (availableAuth & CURLAUTH_NTLM)684 authScheme = ProtectionSpaceAuthenticationSchemeNTLM;685 686 protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);687 688 return true;689 }690 691 size_t ResourceHandleInternal::willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks)692 {693 if (!m_formDataStream.hasMoreElements())694 return 0;695 696 size_t size = m_formDataStream.read(ptr, blockSize, numberOfBlocks);697 698 // Something went wrong so cancel the job.699 if (!size) {700 m_handle->cancel();701 return 0;702 }703 704 return size;705 706 }707 708 void ResourceHandleInternal::didReceiveHeaderLine(const String& header)709 {710 ASSERT(isMainThread());711 712 auto splitPosition = header.find(":");713 if (splitPosition != notFound) {714 String key = header.left(splitPosition).stripWhiteSpace();715 String value = header.substring(splitPosition + 1).stripWhiteSpace();716 717 if (isAppendableHeader(key))718 m_response.addHTTPHeaderField(key, value);719 else720 m_response.setHTTPHeaderField(key, value);721 } else if (header.startsWith("HTTP", false)) {722 // This is the first line of the response.723 // Extract the http status text from this.724 //725 // If the FOLLOWLOCATION option is enabled for the curl handle then726 // curl will follow the redirections internally. Thus this header callback727 // will be called more than one time with the line starting "HTTP" for one job.728 long httpCode = 0;729 m_curlHandle.getResponseCode(httpCode);730 731 String httpCodeString = String::number(httpCode);732 int statusCodePos = header.find(httpCodeString);733 734 if (statusCodePos != notFound) {735 // The status text is after the status code.736 String status = header.substring(statusCodePos + httpCodeString.length());737 m_response.setHTTPStatusText(status.stripWhiteSpace());738 }739 }740 }741 742 void ResourceHandleInternal::didReceiveAllHeaders(long httpCode, long long contentLength)743 {744 ASSERT(isMainThread());745 746 m_response.setExpectedContentLength(contentLength);747 748 m_response.setURL(m_curlHandle.getEffectiveURL());749 750 m_response.setHTTPStatusCode(httpCode);751 m_response.setMimeType(extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());752 m_response.setTextEncodingName(extractCharsetFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)));753 754 if (m_response.isMultipart()) {755 String boundary;756 bool parsed = MultipartHandle::extractBoundary(m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);757 if (parsed)758 m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary);759 }760 761 // HTTP redirection762 if (isHttpRedirect(httpCode)) {763 String location = m_response.httpHeaderField(HTTPHeaderName::Location);764 if (!location.isEmpty()) {765 URL newURL = URL(m_firstRequest.url(), location);766 767 ResourceRequest redirectedRequest = m_firstRequest;768 redirectedRequest.setURL(newURL);769 ResourceResponse response = m_response;770 if (client())771 client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(response));772 773 m_firstRequest.setURL(newURL);774 775 return;776 }777 } else if (isHttpAuthentication(httpCode)) {778 ProtectionSpace protectionSpace;779 if (getProtectionSpace(m_handle, m_response, protectionSpace)) {780 Credential credential;781 AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, m_response, ResourceError());782 challenge.setAuthenticationClient(m_handle);783 m_handle->didReceiveAuthenticationChallenge(challenge);784 m_authFailureCount++;785 return;786 }787 }788 789 if (client()) {790 if (isHttpNotModified(httpCode)) {791 const String& url = m_firstRequest.url().string();792 if (CurlCacheManager::getInstance().getCachedResponse(url, m_response)) {793 if (m_addedCacheValidationHeaders) {794 m_response.setHTTPStatusCode(200);795 m_response.setHTTPStatusText("OK");796 }797 }798 }799 client()->didReceiveResponse(m_handle, ResourceResponse(m_response));800 CurlCacheManager::getInstance().didReceiveResponse(*m_handle, m_response);801 }802 803 m_response.setResponseFired(true);804 }805 806 void ResourceHandleInternal::didReceiveContentData()807 {808 ASSERT(isMainThread());809 810 if (!m_response.responseFired())811 handleLocalReceiveResponse();812 813 Vector<char> buffer;814 {815 LockHolder locker { m_receivedBufferMutex };816 buffer = WTFMove(m_receivedBuffer);817 }818 819 char* ptr = buffer.begin();820 size_t size = buffer.size();821 822 if (m_multipartHandle)823 m_multipartHandle->contentReceived(static_cast<const char*>(ptr), size);824 else if (client()) {825 client()->didReceiveData(m_handle, ptr, size, 0);826 CurlCacheManager::getInstance().didReceiveData(*m_handle, ptr, size);827 }828 }829 830 /* This is called to obtain HTTP POST or PUT data.831 Iterate through FormData elements and upload files.832 Carefully respect the given buffer size and fill the rest of the data at the next calls.833 */834 size_t ResourceHandleInternal::readCallback(char* ptr, size_t size, size_t nmemb, void* data)835 {836 ASSERT(!isMainThread());837 838 ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);839 840 if (d->m_cancelled)841 return 0;842 843 // We should never be called when deferred loading is activated.844 ASSERT(!d->m_defersLoading);845 846 if (!size || !nmemb)847 return 0;848 849 return d->willPrepareSendData(ptr, size, nmemb);850 }851 852 /*853 * This is being called for each HTTP header in the response. This includes '\r\n'854 * for the last line of the header.855 *856 * We will add each HTTP Header to the ResourceResponse and on the termination857 * of the header (\r\n) we will parse Content-Type and Content-Disposition and858 * update the ResourceResponse and then send it away.859 *860 */861 size_t ResourceHandleInternal::headerCallback(char* ptr, size_t size, size_t nmemb, void* data)862 {863 ASSERT(!isMainThread());864 865 ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);866 ResourceHandle* job = d->m_handle;867 868 if (d->m_cancelled)869 return 0;870 871 // We should never be called when deferred loading is activated.872 ASSERT(!d->m_defersLoading);873 874 size_t totalSize = size * nmemb;875 876 String header(static_cast<const char*>(ptr), totalSize);877 878 /*879 * a) We can finish and send the ResourceResponse880 * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse881 *882 * The HTTP standard requires to use \r\n but for compatibility it recommends to883 * accept also \n.884 */885 if (header == AtomicString("\r\n") || header == AtomicString("\n")) {886 long httpCode = 0;887 d->m_curlHandle.getResponseCode(httpCode);888 889 if (!httpCode) {890 // Comes here when receiving 200 Connection Established. Just return.891 return totalSize;892 }893 if (isHttpInfo(httpCode)) {894 // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.895 // If not, the request might be cancelled, because the MIME type will be empty for this response.896 return totalSize;897 }898 899 long long contentLength = 0;900 d->m_curlHandle.getContentLenghtDownload(contentLength);901 902 callOnMainThread([job = RefPtr<ResourceHandle>(job), d, httpCode, contentLength] {903 if (!d->m_cancelled)904 d->didReceiveAllHeaders(httpCode, contentLength);905 });906 } else {907 callOnMainThread([job = RefPtr<ResourceHandle>(job), d, header] {908 if (!d->m_cancelled)909 d->didReceiveHeaderLine(header);910 });911 }912 913 return totalSize;914 }915 916 // called with data after all headers have been processed via headerCallback917 size_t ResourceHandleInternal::writeCallback(char* ptr, size_t size, size_t nmemb, void* data)918 {919 ASSERT(!isMainThread());920 921 ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);922 ResourceHandle* job = d->m_handle;923 924 if (d->m_cancelled)925 return 0;926 927 // We should never be called when deferred loading is activated.928 ASSERT(!d->m_defersLoading);929 930 size_t totalSize = size * nmemb;931 932 // this shouldn't be necessary but apparently is. CURL writes the data933 // of html page even if it is a redirect that was handled internally934 // can be observed e.g. on gmail.com935 long httpCode = 0;936 CURLcode errCd = d->m_curlHandle.getResponseCode(httpCode);937 if (CURLE_OK == errCd && httpCode >= 300 && httpCode < 400)938 return totalSize;939 940 bool shouldCall { false };941 {942 LockHolder locker(d->m_receivedBufferMutex);943 944 if (d->m_receivedBuffer.isEmpty())945 shouldCall = true;946 947 d->m_receivedBuffer.append(ptr, totalSize);948 }949 950 if (shouldCall) {951 callOnMainThread([job = RefPtr<ResourceHandle>(job), d] {952 if (!d->m_cancelled)953 d->didReceiveContentData();954 });955 }956 957 return totalSize;958 }959 960 233 // sync loader 961 234 … … 967 240 RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false)); 968 241 969 handle->d-> dispatchSynchronousJob();242 handle->d->m_delegate->dispatchSynchronousJob(); 970 243 971 244 error = client.error(); … … 974 247 } 975 248 976 void ResourceHandleInternal::dispatchSynchronousJob()977 {978 URL kurl = m_firstRequest.url();979 980 if (kurl.protocolIsData()) {981 handleDataURL();982 return;983 }984 985 // If defersLoading is true and we call curl_easy_perform986 // on a paused handle, libcURL would do the transfert anyway987 // and we would assert so force defersLoading to be false.988 m_defersLoading = false;989 990 initialize();991 992 // curl_easy_perform blocks until the transfert is finished.993 CURLcode ret = m_curlHandle.perform();994 995 calculateWebTimingInformations();996 997 if (client()) {998 if (ret != CURLE_OK)999 client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors()));1000 else1001 client()->didReceiveResponse(m_handle, ResourceResponse(m_response));1002 }1003 }1004 1005 void ResourceHandleInternal::handleDataURL()1006 {1007 ASSERT(m_firstRequest.url().protocolIsData());1008 String url = m_firstRequest.url().string();1009 1010 ASSERT(client());1011 1012 int index = url.find(',');1013 if (index == -1) {1014 client()->cannotShowURL(m_handle);1015 return;1016 }1017 1018 String mediaType = url.substring(5, index - 5);1019 String data = url.substring(index + 1);1020 1021 bool base64 = mediaType.endsWith(";base64", false);1022 if (base64)1023 mediaType = mediaType.left(mediaType.length() - 7);1024 1025 if (mediaType.isEmpty())1026 mediaType = "text/plain";1027 1028 String mimeType = extractMIMETypeFromMediaType(mediaType);1029 String charset = extractCharsetFromMediaType(mediaType);1030 1031 if (charset.isEmpty())1032 charset = "US-ASCII";1033 1034 ResourceResponse response;1035 response.setMimeType(mimeType);1036 response.setTextEncodingName(charset);1037 response.setURL(m_firstRequest.url());1038 1039 if (base64) {1040 data = decodeURLEscapeSequences(data);1041 client()->didReceiveResponse(m_handle, WTFMove(response));1042 1043 // didReceiveResponse might cause the client to be deleted.1044 if (client()) {1045 Vector<char> out;1046 if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)1047 client()->didReceiveData(m_handle, out.data(), out.size(), 0);1048 }1049 } else {1050 TextEncoding encoding(charset);1051 data = decodeURLEscapeSequences(data, encoding);1052 client()->didReceiveResponse(m_handle, WTFMove(response));1053 1054 // didReceiveResponse might cause the client to be deleted.1055 if (client()) {1056 CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);1057 if (encodedData.length())1058 client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0);1059 }1060 }1061 1062 if (client())1063 client()->didFinishLoading(m_handle);1064 }1065 1066 249 } // namespace WebCore 1067 250 -
trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp
r220938 r220939 4 4 * Copyright (C) 2017 Sony Interactive Entertainment Inc. 5 5 * All rights reserved. 6 * Copyright (C) 2017 NAVER Corp. All rights reserved. 6 7 * 7 8 * Redistribution and use in source and binary forms, with or without … … 28 29 29 30 #include "config.h" 30 #include "ResourceHandle .h"31 #include "ResourceHandleCurlDelegate.h" 31 32 32 33 #if USE(CURL) 33 34 34 #include "CachedResourceLoader.h"35 35 #include "CredentialStorage.h" 36 36 #include "CurlCacheManager.h" 37 #include "CurlContext.h"38 #include "CurlJobManager.h"39 #include "FileSystem.h"40 #include "Logging.h"41 37 #include "MIMETypeRegistry.h" 42 #include "NetworkingContext.h" 38 #include "MultipartHandle.h" 39 #include "ResourceHandle.h" 43 40 #include "ResourceHandleInternal.h" 44 41 #include "SSLHandle.h" 45 #include "SynchronousLoaderClient.h" 42 #include "TextEncoding.h" 43 #include "ThreadSafeDataBuffer.h" 44 #include "URL.h" 45 #include <wtf/MainThread.h> 46 46 #include <wtf/text/Base64.h> 47 47 48 48 namespace WebCore { 49 49 50 ResourceHandleInternal::~ResourceHandleInternal() 51 { 52 } 53 54 ResourceHandle::~ResourceHandle() 55 { 56 } 57 58 bool ResourceHandle::start() 59 { 60 ASSERT(isMainThread()); 61 62 // The frame could be null if the ResourceHandle is not associated to any 63 // Frame, e.g. if we are downloading a file. 64 // If the frame is not null but the page is null this must be an attempted 65 // load from an unload handler, so let's just block it. 66 // If both the frame and the page are not null the context is valid. 67 if (d->m_context && !d->m_context->isValid()) 68 return false; 69 70 d->initialize(); 71 72 d->m_job = CurlJobManager::singleton().add(d->m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) { 73 ASSERT(isMainThread()); 74 75 switch (result) { 76 case CurlJobResult::Done: 77 d->didFinish(); 78 break; 79 80 case CurlJobResult::Error: 81 d->didFail(); 82 break; 83 84 case CurlJobResult::Cancelled: 85 break; 86 } 87 }); 88 ASSERT(d->m_job); 89 90 return true; 91 } 92 93 void ResourceHandle::cancel() 94 { 95 d->m_cancelled = true; 96 CurlJobManager::singleton().cancel(d->m_job); 97 } 98 99 void ResourceHandleInternal::initialize() 50 ResourceHandleCurlDelegate::ResourceHandleCurlDelegate(ResourceHandle* handle) 51 : m_handle(handle) 52 , m_formDataStream(handle) 53 , m_firstRequest(handle->firstRequest().isolatedCopy()) 54 , m_customHTTPHeaderFields(m_firstRequest.httpHeaderFields().isolatedCopy()) 55 , m_shouldUseCredentialStorage(handle->shouldUseCredentialStorage()) 56 , m_user(handle->getInternal()->m_user.isolatedCopy()) 57 , m_pass(handle->getInternal()->m_pass.isolatedCopy()) 58 , m_initialCredential(handle->getInternal()->m_initialCredential) 59 , m_defersLoading(handle->getInternal()->m_defersLoading) 60 { 61 const URL& url = m_firstRequest.url(); 62 63 if (url.isLocalFile()) { 64 // Determine the MIME type based on the path. 65 response().setMimeType(MIMETypeRegistry::getMIMETypeForPath(url)); 66 } 67 68 if (m_customHTTPHeaderFields.size()) { 69 auto& cache = CurlCacheManager::getInstance(); 70 bool hasCacheHeaders = m_customHTTPHeaderFields.contains(HTTPHeaderName::IfModifiedSince) || m_customHTTPHeaderFields.contains(HTTPHeaderName::IfNoneMatch); 71 if (!hasCacheHeaders && cache.isCached(url)) { 72 cache.addCacheEntryClient(url, m_handle); 73 // append additional cache information 74 for (auto entry : cache.requestHeaders(url)) 75 m_customHTTPHeaderFields.set(entry.key, entry.value); 76 m_addedCacheValidationHeaders = true; 77 } 78 } 79 80 m_sslClientCertificate = getSSLClientCertificate(url.host()); 81 82 setupAuthentication(); 83 } 84 85 ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate() 86 { 87 } 88 89 bool ResourceHandleCurlDelegate::hasHandle() const 90 { 91 return !!m_handle; 92 } 93 94 void ResourceHandleCurlDelegate::releaseHandle() 95 { 96 m_handle = nullptr; 97 } 98 99 bool ResourceHandleCurlDelegate::start() 100 { 101 m_job = CurlJobManager::singleton().add(m_curlHandle, *this); 102 return !!m_job; 103 } 104 105 void ResourceHandleCurlDelegate::cancel() 106 { 107 releaseHandle(); 108 CurlJobManager::singleton().cancel(m_job); 109 } 110 111 void ResourceHandleCurlDelegate::setDefersLoading(bool defers) 112 { 113 if (defers == m_defersLoading) 114 return; 115 116 m_defersLoading = defers; 117 118 auto action = [protectedThis = makeRef(*this)]() { 119 if (protectedThis->m_defersLoading) { 120 CURLcode error = protectedThis->m_curlHandle.pause(CURLPAUSE_ALL); 121 // If we could not defer the handle, so don't do it. 122 if (error != CURLE_OK) 123 return; 124 } else { 125 CURLcode error = protectedThis->m_curlHandle.pause(CURLPAUSE_CONT); 126 if (error != CURLE_OK) { 127 // Restarting the handle has failed so just cancel it. 128 protectedThis->m_handle->cancel(); 129 } 130 } 131 }; 132 133 CurlJobManager::singleton().callOnJobThread(WTFMove(action)); 134 } 135 136 void ResourceHandleCurlDelegate::setAuthentication(const String& user, const String& pass) 137 { 138 auto action = [protectedThis = makeRef(*this), user = user.isolatedCopy(), pass = pass.isolatedCopy()]() { 139 protectedThis->m_user = user; 140 protectedThis->m_pass = pass; 141 protectedThis->m_curlHandle.setHttpAuthUserPass(user, pass); 142 }; 143 144 CurlJobManager::singleton().callOnJobThread(WTFMove(action)); 145 } 146 147 void ResourceHandleCurlDelegate::dispatchSynchronousJob() 148 { 149 URL kurl = m_firstRequest.url(); 150 151 if (kurl.protocolIsData()) { 152 handleDataURL(); 153 return; 154 } 155 156 // If defersLoading is true and we call curl_easy_perform 157 // on a paused handle, libcURL would do the transfert anyway 158 // and we would assert so force defersLoading to be false. 159 m_defersLoading = false; 160 161 setupRequest(); 162 163 // curl_easy_perform blocks until the transfer is finished. 164 CURLcode ret = m_curlHandle.perform(); 165 166 double pretransferTime = 0; 167 double dnsLookupTime = 0; 168 double connectTime = 0; 169 double appConnectTime = 0; 170 171 m_curlHandle.getTimes(pretransferTime, dnsLookupTime, connectTime, appConnectTime); 172 setWebTimings(pretransferTime, dnsLookupTime, connectTime, appConnectTime); 173 174 if (m_handle->client()) { 175 if (ret != CURLE_OK) { 176 String domain = CurlContext::errorDomain; 177 int errorCode = m_curlHandle.errorCode(); 178 URL failingURL = m_curlHandle.getEffectiveURL(); 179 String errorDescription = m_curlHandle.errorDescription(); 180 unsigned sslErrors = m_curlHandle.getSslErrors(); 181 182 m_handle->client()->didFail(m_handle, ResourceError(domain, errorCode, failingURL, errorDescription, sslErrors)); 183 } else 184 m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response())); 185 } 186 } 187 188 void ResourceHandleCurlDelegate::retain() 189 { 190 ref(); 191 } 192 193 void ResourceHandleCurlDelegate::release() 194 { 195 deref(); 196 } 197 198 void ResourceHandleCurlDelegate::setupRequest() 100 199 { 101 200 CurlContext& context = CurlContext::singleton(); … … 107 206 108 207 String urlString = url.string(); 208 209 m_curlHandle.initialize(); 109 210 110 211 if (url.isLocalFile()) { … … 115 216 urlString = url.string(); 116 217 } 117 // Determine the MIME type based on the path.118 m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));119 218 } 120 219 … … 131 230 #endif 132 231 133 m_curlHandle.initialize();134 232 m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable); 135 233 m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck); 136 234 m_curlHandle.setPrivateData(this); 137 m_curlHandle.setWriteCallbackFunction( writeCallback, this);138 m_curlHandle.setHeaderCallbackFunction( headerCallback, this);235 m_curlHandle.setWriteCallbackFunction(didReceiveDataCallback, this); 236 m_curlHandle.setHeaderCallbackFunction(didReceiveHeaderCallback, this); 139 237 m_curlHandle.enableAutoReferer(); 140 238 m_curlHandle.enableFollowLocation(); … … 143 241 m_curlHandle.enableTimeout(); 144 242 m_curlHandle.enableAllowedProtocols(); 145 auto certificate = getSSLClientCertificate(m_firstRequest.url().host()); 146 if ( certificate) {147 m_curlHandle.setSslCert((* certificate).first.utf8().data());243 244 if (m_sslClientCertificate) { 245 m_curlHandle.setSslCert((*m_sslClientCertificate).first.utf8().data()); 148 246 m_curlHandle.setSslCertType("P12"); 149 m_curlHandle.setSslKeyPassword((* certificate).second.utf8().data());247 m_curlHandle.setSslKeyPassword((*m_sslClientCertificate).second.utf8().data()); 150 248 } 151 249 … … 161 259 m_curlHandle.enableCookieJarIfExists(); 162 260 163 if (m_firstRequest.httpHeaderFields().size()) { 164 auto customHeaders = m_firstRequest.httpHeaderFields(); 165 auto& cache = CurlCacheManager::getInstance(); 166 167 bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch); 168 if (!hasCacheHeaders && cache.isCached(url)) { 169 cache.addCacheEntryClient(url, m_handle); 170 171 // append additional cache information 172 for (auto entry : cache.requestHeaders(url)) 173 customHeaders.set(entry.key, entry.value); 174 175 m_addedCacheValidationHeaders = true; 176 } 177 178 m_curlHandle.appendRequestHeaders(customHeaders); 179 } 261 if (m_customHTTPHeaderFields.size()) 262 m_curlHandle.appendRequestHeaders(m_customHTTPHeaderFields); 180 263 181 264 String method = m_firstRequest.httpMethod(); … … 200 283 } 201 284 202 void ResourceHandleInternal::applyAuthentication() 285 void ResourceHandleCurlDelegate::notifyFinish() 286 { 287 double pretransferTime = 0; 288 double dnsLookupTime = 0; 289 double connectTime = 0; 290 double appConnectTime = 0; 291 292 m_curlHandle.getTimes(pretransferTime, dnsLookupTime, connectTime, appConnectTime); 293 294 callOnMainThread([protectedThis = makeRef(*this), pretransferTime, dnsLookupTime, connectTime, appConnectTime] { 295 if (!protectedThis->m_handle) 296 return; 297 protectedThis->didFinish(pretransferTime, dnsLookupTime, connectTime, appConnectTime); 298 }); 299 } 300 301 void ResourceHandleCurlDelegate::notifyFail() 302 { 303 String domain = CurlContext::errorDomain; 304 int errorCode = m_curlHandle.errorCode(); 305 URL failingURL = m_curlHandle.getEffectiveURL(); 306 String errorDescription = m_curlHandle.errorDescription(); 307 unsigned sslErrors = m_curlHandle.getSslErrors(); 308 309 callOnMainThread([protectedThis = makeRef(*this), domain = domain.isolatedCopy(), errorCode, failingURL = failingURL.isolatedCopy(), errorDescription = errorDescription.isolatedCopy(), sslErrors] { 310 if (!protectedThis->m_handle) 311 return; 312 protectedThis->didFail(domain, errorCode, failingURL, errorDescription, sslErrors); 313 }); 314 } 315 316 ResourceResponse& ResourceHandleCurlDelegate::response() 317 { 318 return m_handle->getInternal()->m_response; 319 } 320 321 void ResourceHandleCurlDelegate::setupAuthentication() 203 322 { 204 323 // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open(). 205 324 String partition = m_firstRequest.cachePartition(); 206 325 207 if (m_ handle->shouldUseCredentialStorage()) {326 if (m_shouldUseCredentialStorage) { 208 327 if (m_user.isEmpty() && m_pass.isEmpty()) { 209 328 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, … … 218 337 } 219 338 } 220 221 String user = m_user; 222 String password = m_pass; 223 224 if (!m_initialCredential.isEmpty()) { 225 user = m_initialCredential.user(); 226 password = m_initialCredential.password(); 227 m_curlHandle.enableHttpAuthentication(CURLAUTH_BASIC); 228 } 229 230 // It seems we need to set CURLOPT_USERPWD even if username and password is empty. 231 // Otherwise cURL will not automatically continue with a new request after a 401 response. 232 233 // curl CURLOPT_USERPWD expects username:password 234 m_curlHandle.setHttpAuthUserPass(user, password); 235 } 236 237 static inline size_t getFormElementsCount(ResourceHandle* job) 238 { 239 RefPtr<FormData> formData = job->firstRequest().httpBody(); 240 241 if (!formData) 242 return 0; 243 244 // Resolve the blob elements so the formData can correctly report it's size. 245 formData = formData->resolveBlobReferences(); 246 size_t size = formData->elements().size(); 247 job->firstRequest().setHTTPBody(WTFMove(formData)); 248 249 return size; 250 } 251 252 void ResourceHandleInternal::setupPUT() 253 { 254 m_curlHandle.enableHttpPutRequest(); 255 256 // Disable the Expect: 100 continue header 257 m_curlHandle.appendRequestHeader("Expect:"); 258 259 size_t numElements = getFormElementsCount(m_handle); 260 if (!numElements) 261 return; 262 263 setupFormData(false); 264 } 265 266 void ResourceHandleInternal::setupPOST() 267 { 268 m_curlHandle.enableHttpPostRequest(); 269 270 size_t numElements = getFormElementsCount(m_handle); 271 if (!numElements) 272 return; 273 274 // Do not stream for simple POST data 275 if (numElements == 1) { 276 m_firstRequest.httpBody()->flatten(m_postBytes); 277 if (m_postBytes.size()) 278 m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size()); 279 return; 280 } 281 282 setupFormData(true); 283 } 284 285 void ResourceHandleInternal::setupFormData(bool isPostRequest) 286 { 287 Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements(); 288 size_t numElements = elements.size(); 289 290 static const long long maxCurlOffT = m_curlHandle.maxCurlOffT(); 291 292 // Obtain the total size of the form data 293 curl_off_t size = 0; 294 bool chunkedTransfer = false; 295 for (size_t i = 0; i < numElements; i++) { 296 FormDataElement element = elements[i]; 297 if (element.m_type == FormDataElement::Type::EncodedFile) { 298 long long fileSizeResult; 299 if (getFileSize(element.m_filename, fileSizeResult)) { 300 if (fileSizeResult > maxCurlOffT) { 301 // File size is too big for specifying it to cURL 302 chunkedTransfer = true; 303 break; 304 } 305 size += fileSizeResult; 306 } else { 307 chunkedTransfer = true; 308 break; 309 } 310 } else 311 size += elements[i].m_data.size(); 312 } 313 314 // cURL guesses that we want chunked encoding as long as we specify the header 315 if (chunkedTransfer) 316 m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked"); 317 else { 318 if (isPostRequest) 319 m_curlHandle.setPostFieldLarge(size); 320 else 321 m_curlHandle.setInFileSizeLarge(size); 322 } 323 324 m_curlHandle.setReadCallbackFunction(readCallback, this); 325 } 326 327 #if OS(WINDOWS) 328 329 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host) 330 { 331 ASSERT(isMainThread()); 332 333 allowsAnyHTTPSCertificateHosts(host); 334 } 335 336 void ResourceHandle::setClientCertificateInfo(const String& host, const String& certificate, const String& key) 337 { 338 ASSERT(isMainThread()); 339 340 if (fileExists(certificate)) 341 addAllowedClientCertificate(host, certificate, key); 342 else 343 LOG(Network, "Invalid client certificate file: %s!\n", certificate.latin1().data()); 344 } 345 346 #endif 347 348 #if OS(WINDOWS) && USE(CF) 349 350 void ResourceHandle::setClientCertificate(const String&, CFDataRef) 351 { 352 } 353 354 #endif 355 356 void ResourceHandle::platformSetDefersLoading(bool defers) 357 { 358 ASSERT(isMainThread()); 359 360 auto action = [defers, this, protectedThis = makeRef(*this)]() { 361 if (defers) { 362 CURLcode error = d->m_curlHandle.pause(CURLPAUSE_ALL); 363 // If we could not defer the handle, so don't do it. 364 if (error != CURLE_OK) 365 return; 366 } else { 367 CURLcode error = d->m_curlHandle.pause(CURLPAUSE_CONT); 368 if (error != CURLE_OK) { 369 // Restarting the handle has failed so just cancel it. 370 cancel(); 371 } 372 } 373 }; 374 375 if (d->m_job) { 376 CurlJobManager::singleton().callOnJobThread(WTFMove(action)); 377 } else { 378 action(); 379 } 380 } 381 382 void ResourceHandleInternal::didFinish() 383 { 384 calculateWebTimingInformations(); 385 386 if (m_cancelled) 387 return; 388 389 if (!m_response.responseFired()) { 390 handleLocalReceiveResponse(); 391 if (m_cancelled) 392 return; 393 } 394 395 if (m_multipartHandle) 396 m_multipartHandle->contentEnded(); 397 398 if (client()) { 399 client()->didFinishLoading(m_handle); 400 CurlCacheManager::getInstance().didFinishLoading(*m_handle); 401 } 402 } 403 404 void ResourceHandleInternal::didFail() 405 { 406 if (m_cancelled) 407 return; 339 } 340 341 static void removeLeadingAndTrailingQuotes(String& value) 342 { 343 unsigned length = value.length(); 344 if (value.startsWith('"') && value.endsWith('"') && length > 1) 345 value = value.substring(1, length - 2); 346 } 347 348 bool ResourceHandleCurlDelegate::getProtectionSpace(const ResourceResponse& response, ProtectionSpace& protectionSpace) 349 { 350 CURLcode err; 351 352 long port = 0; 353 err = m_curlHandle.getPrimaryPort(port); 354 if (err != CURLE_OK) 355 return false; 356 357 long availableAuth = CURLAUTH_NONE; 358 err = m_curlHandle.getHttpAuthAvail(availableAuth); 359 if (err != CURLE_OK) 360 return false; 361 408 362 URL url = m_curlHandle.getEffectiveURL(); 409 if (client()) { 410 client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors())); 411 CurlCacheManager::getInstance().didFail(*m_handle); 412 } 413 } 414 415 bool ResourceHandle::shouldUseCredentialStorage() 416 { 417 return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily(); 418 } 419 420 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) 421 { 422 ASSERT(isMainThread()); 423 424 String partition = firstRequest().cachePartition(); 425 426 if (!d->m_user.isNull() && !d->m_pass.isNull()) { 427 Credential credential(d->m_user, d->m_pass, CredentialPersistenceNone); 428 429 URL urlToStore; 430 if (challenge.failureResponse().httpStatusCode() == 401) 431 urlToStore = challenge.failureResponse().url(); 432 CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore); 433 434 d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password()); 435 436 d->m_user = String(); 437 d->m_pass = String(); 438 // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. 439 return; 440 } 441 442 if (shouldUseCredentialStorage()) { 443 if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) { 444 // The stored credential wasn't accepted, stop using it. 445 // There is a race condition here, since a different credential might have already been stored by another ResourceHandle, 446 // but the observable effect should be very minor, if any. 447 CredentialStorage::defaultCredentialStorage().remove(partition, challenge.protectionSpace()); 448 } 449 450 if (!challenge.previousFailureCount()) { 451 Credential credential = CredentialStorage::defaultCredentialStorage().get(partition, challenge.protectionSpace()); 452 if (!credential.isEmpty() && credential != d->m_initialCredential) { 453 ASSERT(credential.persistence() == CredentialPersistenceNone); 454 if (challenge.failureResponse().httpStatusCode() == 401) { 455 // Store the credential back, possibly adding it as a default for this directory. 456 CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url()); 457 } 458 459 d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password()); 460 return; 461 } 462 } 463 } 464 465 d->m_currentWebChallenge = challenge; 466 467 if (client()) 468 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge); 469 } 470 471 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential) 472 { 473 ASSERT(isMainThread()); 474 475 if (challenge != d->m_currentWebChallenge) 476 return; 477 478 if (credential.isEmpty()) { 479 receivedRequestToContinueWithoutCredential(challenge); 480 return; 481 } 482 483 String partition = firstRequest().cachePartition(); 484 485 if (shouldUseCredentialStorage()) { 486 if (challenge.failureResponse().httpStatusCode() == 401) { 487 URL urlToStore = challenge.failureResponse().url(); 488 CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore); 489 } 490 } 491 492 d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password()); 493 clearAuthentication(); 494 } 495 496 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge) 497 { 498 ASSERT(isMainThread()); 499 500 if (challenge != d->m_currentWebChallenge) 501 return; 502 503 d->m_curlHandle.setHttpAuthUserPass("", ""); 504 clearAuthentication(); 505 } 506 507 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge) 508 { 509 ASSERT(isMainThread()); 510 511 if (challenge != d->m_currentWebChallenge) 512 return; 513 514 if (client()) 515 client()->receivedCancellation(this, challenge); 516 } 517 518 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) 519 { 520 ASSERT_NOT_REACHED(); 521 } 522 523 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge&) 524 { 525 ASSERT_NOT_REACHED(); 526 } 527 528 void ResourceHandleInternal::calculateWebTimingInformations() 529 { 530 double preTransferTime = 0; 531 double dnslookupTime = 0; 532 double connectTime = 0; 533 double appConnectTime = 0; 534 535 m_curlHandle.getTimes(preTransferTime, dnslookupTime, connectTime, appConnectTime); 536 537 m_response.deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0); 538 m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnslookupTime); 539 540 m_response.deprecatedNetworkLoadMetrics().connectStart = Seconds(dnslookupTime); 541 m_response.deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime); 542 543 m_response.deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime); 544 m_response.deprecatedNetworkLoadMetrics().responseStart = Seconds(preTransferTime); 545 546 if (appConnectTime) 547 m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime); 548 } 549 550 void ResourceHandleInternal::handleLocalReceiveResponse() 551 { 552 ASSERT(isMainThread()); 553 554 // since the code in headerCallback will not have run for local files 555 // the code to set the URL and fire didReceiveResponse is never run, 556 // which means the ResourceLoader's response does not contain the URL. 557 // Run the code here for local files to resolve the issue. 558 // TODO: See if there is a better approach for handling this. 559 URL url = m_curlHandle.getEffectiveURL(); 560 ASSERT(url.isValid()); 561 m_response.setURL(url); 562 if (client()) 563 client()->didReceiveResponse(m_handle, ResourceResponse(m_response)); 564 m_response.setResponseFired(true); 363 if (!url.isValid()) 364 return false; 365 366 String host = url.host(); 367 StringView protocol = url.protocol(); 368 369 String realm; 370 371 const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization); 372 const String realmString = "realm="; 373 int realmPos = authHeader.find(realmString); 374 if (realmPos > 0) { 375 realm = authHeader.substring(realmPos + realmString.length()); 376 realm = realm.left(realm.find(',')); 377 removeLeadingAndTrailingQuotes(realm); 378 } 379 380 ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP; 381 if (protocol == "https") 382 serverType = ProtectionSpaceServerHTTPS; 383 384 ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown; 385 386 if (availableAuth & CURLAUTH_BASIC) 387 authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic; 388 if (availableAuth & CURLAUTH_DIGEST) 389 authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest; 390 if (availableAuth & CURLAUTH_GSSNEGOTIATE) 391 authScheme = ProtectionSpaceAuthenticationSchemeNegotiate; 392 if (availableAuth & CURLAUTH_NTLM) 393 authScheme = ProtectionSpaceAuthenticationSchemeNTLM; 394 395 protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme); 396 397 return true; 565 398 } 566 399 … … 628 461 } 629 462 630 static void removeLeadingAndTrailingQuotes(String& value) 631 { 632 unsigned length = value.length(); 633 if (value.startsWith('"') && value.endsWith('"') && length > 1) 634 value = value.substring(1, length - 2); 635 } 636 637 static bool getProtectionSpace(ResourceHandle* job, const ResourceResponse& response, ProtectionSpace& protectionSpace) 638 { 639 ResourceHandleInternal* d = job->getInternal(); 640 641 CURLcode err; 642 643 long port = 0; 644 err = d->m_curlHandle.getPrimaryPort(port); 645 if (err != CURLE_OK) 646 return false; 647 648 long availableAuth = CURLAUTH_NONE; 649 err = d->m_curlHandle.getHttpAuthAvail(availableAuth); 650 if (err != CURLE_OK) 651 return false; 652 653 URL url = d->m_curlHandle.getEffectiveURL(); 654 if (!url.isValid()) 655 return false; 656 657 String host = url.host(); 658 StringView protocol = url.protocol(); 659 660 String realm; 661 662 const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization); 663 const String realmString = "realm="; 664 int realmPos = authHeader.find(realmString); 665 if (realmPos > 0) { 666 realm = authHeader.substring(realmPos + realmString.length()); 667 realm = realm.left(realm.find(',')); 668 removeLeadingAndTrailingQuotes(realm); 669 } 670 671 ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP; 672 if (protocol == "https") 673 serverType = ProtectionSpaceServerHTTPS; 674 675 ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown; 676 677 if (availableAuth & CURLAUTH_BASIC) 678 authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic; 679 if (availableAuth & CURLAUTH_DIGEST) 680 authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest; 681 if (availableAuth & CURLAUTH_GSSNEGOTIATE) 682 authScheme = ProtectionSpaceAuthenticationSchemeNegotiate; 683 if (availableAuth & CURLAUTH_NTLM) 684 authScheme = ProtectionSpaceAuthenticationSchemeNTLM; 685 686 protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme); 687 688 return true; 689 } 690 691 size_t ResourceHandleInternal::willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks) 692 { 693 if (!m_formDataStream.hasMoreElements()) 694 return 0; 695 696 size_t size = m_formDataStream.read(ptr, blockSize, numberOfBlocks); 697 698 // Something went wrong so cancel the job. 699 if (!size) { 700 m_handle->cancel(); 701 return 0; 702 } 703 704 return size; 705 706 } 707 708 void ResourceHandleInternal::didReceiveHeaderLine(const String& header) 463 void ResourceHandleCurlDelegate::didReceiveHeaderLine(const String& header) 709 464 { 710 465 ASSERT(isMainThread()); … … 716 471 717 472 if (isAppendableHeader(key)) 718 m_response.addHTTPHeaderField(key, value);473 response().addHTTPHeaderField(key, value); 719 474 else 720 m_response.setHTTPHeaderField(key, value);475 response().setHTTPHeaderField(key, value); 721 476 } else if (header.startsWith("HTTP", false)) { 722 477 // This is the first line of the response. … … 735 490 // The status text is after the status code. 736 491 String status = header.substring(statusCodePos + httpCodeString.length()); 737 m_response.setHTTPStatusText(status.stripWhiteSpace());738 } 739 } 740 } 741 742 void ResourceHandle Internal::didReceiveAllHeaders(long httpCode, long long contentLength)492 response().setHTTPStatusText(status.stripWhiteSpace()); 493 } 494 } 495 } 496 497 void ResourceHandleCurlDelegate::didReceiveAllHeaders(long httpCode, long long contentLength) 743 498 { 744 499 ASSERT(isMainThread()); 745 500 746 m_response.setExpectedContentLength(contentLength); 747 748 m_response.setURL(m_curlHandle.getEffectiveURL()); 749 750 m_response.setHTTPStatusCode(httpCode); 751 m_response.setMimeType(extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase()); 752 m_response.setTextEncodingName(extractCharsetFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType))); 753 754 if (m_response.isMultipart()) { 501 response().setExpectedContentLength(contentLength); 502 response().setURL(m_curlHandle.getEffectiveURL()); 503 response().setHTTPStatusCode(httpCode); 504 response().setMimeType(extractMIMETypeFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase()); 505 response().setTextEncodingName(extractCharsetFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType))); 506 507 if (response().isMultipart()) { 755 508 String boundary; 756 bool parsed = MultipartHandle::extractBoundary( m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);509 bool parsed = MultipartHandle::extractBoundary(response().httpHeaderField(HTTPHeaderName::ContentType), boundary); 757 510 if (parsed) 758 511 m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary); … … 761 514 // HTTP redirection 762 515 if (isHttpRedirect(httpCode)) { 763 String location = m_response.httpHeaderField(HTTPHeaderName::Location);516 String location = response().httpHeaderField(HTTPHeaderName::Location); 764 517 if (!location.isEmpty()) { 765 518 URL newURL = URL(m_firstRequest.url(), location); … … 767 520 ResourceRequest redirectedRequest = m_firstRequest; 768 521 redirectedRequest.setURL(newURL); 769 ResourceResponse response = m_response;770 if ( client())771 client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(response));522 ResourceResponse localResponse = response(); 523 if (m_handle->client()) 524 m_handle->client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(localResponse)); 772 525 773 526 m_firstRequest.setURL(newURL); … … 777 530 } else if (isHttpAuthentication(httpCode)) { 778 531 ProtectionSpace protectionSpace; 779 if (getProtectionSpace( m_handle, m_response, protectionSpace)) {532 if (getProtectionSpace(response(), protectionSpace)) { 780 533 Credential credential; 781 AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, m_response, ResourceError());534 AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, response(), ResourceError()); 782 535 challenge.setAuthenticationClient(m_handle); 783 536 m_handle->didReceiveAuthenticationChallenge(challenge); … … 787 540 } 788 541 789 if (client()) { 542 response().setResponseFired(true); 543 544 if (m_handle->client()) { 790 545 if (isHttpNotModified(httpCode)) { 791 546 const String& url = m_firstRequest.url().string(); 792 if (CurlCacheManager::getInstance().getCachedResponse(url, m_response)) {547 if (CurlCacheManager::getInstance().getCachedResponse(url, response())) { 793 548 if (m_addedCacheValidationHeaders) { 794 m_response.setHTTPStatusCode(200);795 m_response.setHTTPStatusText("OK");549 response().setHTTPStatusCode(200); 550 response().setHTTPStatusText("OK"); 796 551 } 797 552 } 798 553 } 799 client()->didReceiveResponse(m_handle, ResourceResponse(m_response)); 800 CurlCacheManager::getInstance().didReceiveResponse(*m_handle, m_response); 801 } 802 803 m_response.setResponseFired(true); 804 } 805 806 void ResourceHandleInternal::didReceiveContentData() 554 CurlCacheManager::getInstance().didReceiveResponse(*m_handle, response()); 555 m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response())); 556 } 557 } 558 559 void ResourceHandleCurlDelegate::didReceiveContentData(ThreadSafeDataBuffer buffer) 807 560 { 808 561 ASSERT(isMainThread()); 809 562 810 if (! m_response.responseFired())563 if (!response().responseFired()) 811 564 handleLocalReceiveResponse(); 812 565 813 Vector<char> buffer; 814 { 815 LockHolder locker { m_receivedBufferMutex }; 816 buffer = WTFMove(m_receivedBuffer); 817 } 818 819 char* ptr = buffer.begin(); 566 const char* ptr = reinterpret_cast<const char*>(buffer.data()->begin()); 820 567 size_t size = buffer.size(); 821 568 822 569 if (m_multipartHandle) 823 m_multipartHandle->contentReceived(static_cast<const char*>(ptr), size); 824 else if (client()) { 825 client()->didReceiveData(m_handle, ptr, size, 0); 570 m_multipartHandle->contentReceived(ptr, size); 571 else if (m_handle->client()) { 826 572 CurlCacheManager::getInstance().didReceiveData(*m_handle, ptr, size); 827 } 828 } 829 830 /* This is called to obtain HTTP POST or PUT data. 831 Iterate through FormData elements and upload files. 832 Carefully respect the given buffer size and fill the rest of the data at the next calls. 833 */ 834 size_t ResourceHandleInternal::readCallback(char* ptr, size_t size, size_t nmemb, void* data) 835 { 836 ASSERT(!isMainThread()); 837 838 ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data); 839 840 if (d->m_cancelled) 573 m_handle->client()->didReceiveData(m_handle, ptr, size, 0); 574 } 575 } 576 577 void ResourceHandleCurlDelegate::handleLocalReceiveResponse() 578 { 579 ASSERT(isMainThread()); 580 581 // since the code in headerCallback will not have run for local files 582 // the code to set the URL and fire didReceiveResponse is never run, 583 // which means the ResourceLoader's response does not contain the URL. 584 // Run the code here for local files to resolve the issue. 585 // TODO: See if there is a better approach for handling this. 586 URL url = m_curlHandle.getEffectiveURL(); 587 ASSERT(url.isValid()); 588 response().setURL(url); 589 response().setResponseFired(true); 590 if (m_handle->client()) 591 m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response())); 592 } 593 594 void ResourceHandleCurlDelegate::prepareSendData(char* buffer, size_t blockSize, size_t numberOfBlocks) 595 { 596 ASSERT(isMainThread()); 597 ASSERT(!m_sendBytes); 598 599 std::unique_lock<Lock> lock(m_workerThreadMutex); 600 601 if (!m_formDataStream.hasMoreElements()) 602 return; 603 604 size_t size = m_formDataStream.read(buffer, blockSize, numberOfBlocks); 605 if (!size) { 606 // Something went wrong so cancel the job. 607 m_handle->cancel(); 608 return; 609 } 610 611 m_sendBytes = size; 612 m_workerThreadConditionVariable.notifyOne(); 613 } 614 615 void ResourceHandleCurlDelegate::didFinish(double pretransferTime, double dnsLookupTime, double connectTime, double appConnectTime) 616 { 617 setWebTimings(pretransferTime, dnsLookupTime, connectTime, appConnectTime); 618 619 if (!m_handle) 620 return; 621 622 if (!response().responseFired()) { 623 handleLocalReceiveResponse(); 624 if (!m_handle) 625 return; 626 } 627 628 if (m_multipartHandle) 629 m_multipartHandle->contentEnded(); 630 631 if (m_handle->client()) { 632 CurlCacheManager::getInstance().didFinishLoading(*m_handle); 633 m_handle->client()->didFinishLoading(m_handle); 634 } 635 } 636 637 void ResourceHandleCurlDelegate::didFail(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, unsigned sslErrors) 638 { 639 if (!m_handle) 640 return; 641 642 if (m_handle->client()) { 643 CurlCacheManager::getInstance().didFail(*m_handle); 644 m_handle->client()->didFail(m_handle, ResourceError(domain, errorCode, failingURL, localizedDescription, sslErrors)); 645 } 646 } 647 648 void ResourceHandleCurlDelegate::handleDataURL() 649 { 650 ASSERT(m_firstRequest.url().protocolIsData()); 651 String url = m_firstRequest.url().string(); 652 653 ASSERT(m_handle->client()); 654 655 int index = url.find(','); 656 if (index == -1) { 657 m_handle->client()->cannotShowURL(m_handle); 658 return; 659 } 660 661 String mediaType = url.substring(5, index - 5); 662 String data = url.substring(index + 1); 663 664 bool base64 = mediaType.endsWith(";base64", false); 665 if (base64) 666 mediaType = mediaType.left(mediaType.length() - 7); 667 668 if (mediaType.isEmpty()) 669 mediaType = "text/plain"; 670 671 String mimeType = extractMIMETypeFromMediaType(mediaType); 672 String charset = extractCharsetFromMediaType(mediaType); 673 674 if (charset.isEmpty()) 675 charset = "US-ASCII"; 676 677 ResourceResponse response; 678 response.setMimeType(mimeType); 679 response.setTextEncodingName(charset); 680 response.setURL(m_firstRequest.url()); 681 682 if (base64) { 683 data = decodeURLEscapeSequences(data); 684 m_handle->client()->didReceiveResponse(m_handle, WTFMove(response)); 685 686 // didReceiveResponse might cause the client to be deleted. 687 if (m_handle->client()) { 688 Vector<char> out; 689 if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0) 690 m_handle->client()->didReceiveData(m_handle, out.data(), out.size(), 0); 691 } 692 } else { 693 TextEncoding encoding(charset); 694 data = decodeURLEscapeSequences(data, encoding); 695 m_handle->client()->didReceiveResponse(m_handle, WTFMove(response)); 696 697 // didReceiveResponse might cause the client to be deleted. 698 if (m_handle->client()) { 699 CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables); 700 if (encodedData.length()) 701 m_handle->client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0); 702 } 703 } 704 705 if (m_handle->client()) 706 m_handle->client()->didFinishLoading(m_handle); 707 } 708 709 void ResourceHandleCurlDelegate::setupPOST() 710 { 711 m_curlHandle.enableHttpPostRequest(); 712 713 size_t numElements = getFormElementsCount(); 714 if (!numElements) 715 return; 716 717 // Do not stream for simple POST data 718 if (numElements == 1) { 719 m_firstRequest.httpBody()->flatten(m_postBytes); 720 if (m_postBytes.size()) 721 m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size()); 722 return; 723 } 724 725 setupFormData(true); 726 } 727 728 void ResourceHandleCurlDelegate::setupPUT() 729 { 730 m_curlHandle.enableHttpPutRequest(); 731 732 // Disable the Expect: 100 continue header 733 m_curlHandle.appendRequestHeader("Expect:"); 734 735 size_t numElements = getFormElementsCount(); 736 if (!numElements) 737 return; 738 739 setupFormData(false); 740 } 741 742 size_t ResourceHandleCurlDelegate::getFormElementsCount() 743 { 744 RefPtr<FormData> formData = m_firstRequest.httpBody(); 745 if (!formData) 841 746 return 0; 842 747 843 // We should never be called when deferred loading is activated. 844 ASSERT(!d->m_defersLoading); 845 846 if (!size || !nmemb) 847 return 0; 848 849 return d->willPrepareSendData(ptr, size, nmemb); 748 // Resolve the blob elements so the formData can correctly report it's size. 749 formData = formData->resolveBlobReferences(); 750 size_t size = formData->elements().size(); 751 m_firstRequest.setHTTPBody(WTFMove(formData)); 752 return size; 753 } 754 755 void ResourceHandleCurlDelegate::setupFormData(bool isPostRequest) 756 { 757 Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements(); 758 size_t numElements = elements.size(); 759 760 static const long long maxCurlOffT = m_curlHandle.maxCurlOffT(); 761 762 // Obtain the total size of the form data 763 curl_off_t size = 0; 764 bool chunkedTransfer = false; 765 for (size_t i = 0; i < numElements; i++) { 766 FormDataElement element = elements[i]; 767 if (element.m_type == FormDataElement::Type::EncodedFile) { 768 long long fileSizeResult; 769 if (getFileSize(element.m_filename, fileSizeResult)) { 770 if (fileSizeResult > maxCurlOffT) { 771 // File size is too big for specifying it to cURL 772 chunkedTransfer = true; 773 break; 774 } 775 size += fileSizeResult; 776 } else { 777 chunkedTransfer = true; 778 break; 779 } 780 } else 781 size += elements[i].m_data.size(); 782 } 783 784 // cURL guesses that we want chunked encoding as long as we specify the header 785 if (chunkedTransfer) 786 m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked"); 787 else { 788 if (isPostRequest) 789 m_curlHandle.setPostFieldLarge(size); 790 else 791 m_curlHandle.setInFileSizeLarge(size); 792 } 793 794 m_curlHandle.setReadCallbackFunction(willSendDataCallback, this); 795 } 796 797 void ResourceHandleCurlDelegate::applyAuthentication() 798 { 799 String user = m_user; 800 String password = m_pass; 801 802 if (!m_initialCredential.isEmpty()) { 803 user = m_initialCredential.user(); 804 password = m_initialCredential.password(); 805 m_curlHandle.enableHttpAuthentication(CURLAUTH_BASIC); 806 } 807 808 // It seems we need to set CURLOPT_USERPWD even if username and password is empty. 809 // Otherwise cURL will not automatically continue with a new request after a 401 response. 810 811 // curl CURLOPT_USERPWD expects username:password 812 m_curlHandle.setHttpAuthUserPass(user, password); 813 } 814 815 void ResourceHandleCurlDelegate::setWebTimings(double pretransferTime, double dnsLookupTime, double connectTime, double appConnectTime) 816 { 817 response().deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0); 818 response().deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnsLookupTime); 819 820 response().deprecatedNetworkLoadMetrics().connectStart = Seconds(dnsLookupTime); 821 response().deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime); 822 823 response().deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime); 824 response().deprecatedNetworkLoadMetrics().responseStart = Seconds(pretransferTime); 825 826 if (appConnectTime) 827 response().deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime); 850 828 } 851 829 … … 859 837 * 860 838 */ 861 size_t ResourceHandleInternal::headerCallback(char* ptr, size_t size, size_t nmemb, void* data) 862 { 863 ASSERT(!isMainThread()); 864 865 ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data); 866 ResourceHandle* job = d->m_handle; 867 868 if (d->m_cancelled) 839 size_t ResourceHandleCurlDelegate::didReceiveHeader(String&& header) 840 { 841 if (!m_handle) 869 842 return 0; 870 843 871 // We should never be called when deferred loading is activated. 872 ASSERT(!d->m_defersLoading); 873 874 size_t totalSize = size * nmemb; 875 876 String header(static_cast<const char*>(ptr), totalSize); 844 if (m_defersLoading) 845 return 0; 877 846 878 847 /* … … 885 854 if (header == AtomicString("\r\n") || header == AtomicString("\n")) { 886 855 long httpCode = 0; 887 d->m_curlHandle.getResponseCode(httpCode);856 m_curlHandle.getResponseCode(httpCode); 888 857 889 858 if (!httpCode) { 890 859 // Comes here when receiving 200 Connection Established. Just return. 891 return totalSize;860 return header.length(); 892 861 } 893 862 if (isHttpInfo(httpCode)) { 894 863 // Just return when receiving http info, e.g. HTTP/1.1 100 Continue. 895 864 // If not, the request might be cancelled, because the MIME type will be empty for this response. 896 return totalSize;865 return header.length(); 897 866 } 898 867 899 868 long long contentLength = 0; 900 d->m_curlHandle.getContentLenghtDownload(contentLength); 901 902 callOnMainThread([job = RefPtr<ResourceHandle>(job), d, httpCode, contentLength] { 903 if (!d->m_cancelled) 904 d->didReceiveAllHeaders(httpCode, contentLength); 869 m_curlHandle.getContentLenghtDownload(contentLength); 870 871 callOnMainThread([protectedThis = makeRef(*this), httpCode, contentLength] { 872 if (!protectedThis->m_handle) 873 return; 874 protectedThis->didReceiveAllHeaders(httpCode, contentLength); 905 875 }); 906 876 } else { 907 callOnMainThread([job = RefPtr<ResourceHandle>(job), d, header] { 908 if (!d->m_cancelled) 909 d->didReceiveHeaderLine(header); 877 callOnMainThread([protectedThis = makeRef(*this), header = header.isolatedCopy() ] { 878 if (!protectedThis->m_handle) 879 return; 880 protectedThis->didReceiveHeaderLine(header); 910 881 }); 911 882 } 912 883 913 return totalSize;884 return header.length(); 914 885 } 915 886 916 887 // called with data after all headers have been processed via headerCallback 917 size_t ResourceHandleInternal::writeCallback(char* ptr, size_t size, size_t nmemb, void* data) 918 { 919 ASSERT(!isMainThread()); 920 921 ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data); 922 ResourceHandle* job = d->m_handle; 923 924 if (d->m_cancelled) 888 size_t ResourceHandleCurlDelegate::didReceiveData(ThreadSafeDataBuffer data) 889 { 890 if (!m_handle) 925 891 return 0; 926 892 927 // We should never be called when deferred loading is activated. 928 ASSERT(!d->m_defersLoading); 929 930 size_t totalSize = size * nmemb; 893 if (m_defersLoading) 894 return 0; 931 895 932 896 // this shouldn't be necessary but apparently is. CURL writes the data … … 934 898 // can be observed e.g. on gmail.com 935 899 long httpCode = 0; 936 CURLcode errCd = d->m_curlHandle.getResponseCode(httpCode);900 CURLcode errCd = m_curlHandle.getResponseCode(httpCode); 937 901 if (CURLE_OK == errCd && httpCode >= 300 && httpCode < 400) 938 return totalSize; 939 940 bool shouldCall { false }; 902 return data.size(); 903 904 if (!data.size()) 905 return 0; 906 907 callOnMainThread([protectedThis = makeRef(*this), data] { 908 if (!protectedThis->m_handle) 909 return; 910 protectedThis->didReceiveContentData(data); 911 }); 912 913 return data.size(); 914 } 915 916 /* This is called to obtain HTTP POST or PUT data. 917 Iterate through FormData elements and upload files. 918 Carefully respect the given buffer blockSize and fill the rest of the data at the next calls. 919 */ 920 size_t ResourceHandleCurlDelegate::willSendData(char* buffer, size_t blockSize, size_t numberOfBlocks) 921 { 922 ASSERT(!isMainThread()); 923 924 if (!m_handle) 925 return 0; 926 927 if (m_defersLoading) 928 return 0; 929 930 if (!blockSize || !numberOfBlocks) 931 return 0; 932 941 933 { 942 LockHolder locker(d->m_receivedBufferMutex); 943 944 if (d->m_receivedBuffer.isEmpty()) 945 shouldCall = true; 946 947 d->m_receivedBuffer.append(ptr, totalSize); 948 } 949 950 if (shouldCall) { 951 callOnMainThread([job = RefPtr<ResourceHandle>(job), d] { 952 if (!d->m_cancelled) 953 d->didReceiveContentData(); 934 std::unique_lock<Lock> lock(m_workerThreadMutex); 935 936 m_sendBytes = 0; 937 938 callOnMainThread([protectedThis = makeRef(*this), buffer, blockSize, numberOfBlocks] { 939 if (!protectedThis->m_handle) 940 return; 941 protectedThis->prepareSendData(buffer, blockSize, numberOfBlocks); 954 942 }); 955 } 956 957 return totalSize; 958 } 959 960 // sync loader 961 962 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) 963 { 964 ASSERT(isMainThread()); 965 966 SynchronousLoaderClient client; 967 RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false)); 968 969 handle->d->dispatchSynchronousJob(); 970 971 error = client.error(); 972 data.swap(client.mutableData()); 973 response = client.response(); 974 } 975 976 void ResourceHandleInternal::dispatchSynchronousJob() 977 { 978 URL kurl = m_firstRequest.url(); 979 980 if (kurl.protocolIsData()) { 981 handleDataURL(); 982 return; 983 } 984 985 // If defersLoading is true and we call curl_easy_perform 986 // on a paused handle, libcURL would do the transfert anyway 987 // and we would assert so force defersLoading to be false. 988 m_defersLoading = false; 989 990 initialize(); 991 992 // curl_easy_perform blocks until the transfert is finished. 993 CURLcode ret = m_curlHandle.perform(); 994 995 calculateWebTimingInformations(); 996 997 if (client()) { 998 if (ret != CURLE_OK) 999 client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors())); 1000 else 1001 client()->didReceiveResponse(m_handle, ResourceResponse(m_response)); 1002 } 1003 } 1004 1005 void ResourceHandleInternal::handleDataURL() 1006 { 1007 ASSERT(m_firstRequest.url().protocolIsData()); 1008 String url = m_firstRequest.url().string(); 1009 1010 ASSERT(client()); 1011 1012 int index = url.find(','); 1013 if (index == -1) { 1014 client()->cannotShowURL(m_handle); 1015 return; 1016 } 1017 1018 String mediaType = url.substring(5, index - 5); 1019 String data = url.substring(index + 1); 1020 1021 bool base64 = mediaType.endsWith(";base64", false); 1022 if (base64) 1023 mediaType = mediaType.left(mediaType.length() - 7); 1024 1025 if (mediaType.isEmpty()) 1026 mediaType = "text/plain"; 1027 1028 String mimeType = extractMIMETypeFromMediaType(mediaType); 1029 String charset = extractCharsetFromMediaType(mediaType); 1030 1031 if (charset.isEmpty()) 1032 charset = "US-ASCII"; 1033 1034 ResourceResponse response; 1035 response.setMimeType(mimeType); 1036 response.setTextEncodingName(charset); 1037 response.setURL(m_firstRequest.url()); 1038 1039 if (base64) { 1040 data = decodeURLEscapeSequences(data); 1041 client()->didReceiveResponse(m_handle, WTFMove(response)); 1042 1043 // didReceiveResponse might cause the client to be deleted. 1044 if (client()) { 1045 Vector<char> out; 1046 if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0) 1047 client()->didReceiveData(m_handle, out.data(), out.size(), 0); 1048 } 1049 } else { 1050 TextEncoding encoding(charset); 1051 data = decodeURLEscapeSequences(data, encoding); 1052 client()->didReceiveResponse(m_handle, WTFMove(response)); 1053 1054 // didReceiveResponse might cause the client to be deleted. 1055 if (client()) { 1056 CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables); 1057 if (encodedData.length()) 1058 client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0); 1059 } 1060 } 1061 1062 if (client()) 1063 client()->didFinishLoading(m_handle); 943 944 m_workerThreadConditionVariable.wait(lock, [this] { 945 return m_sendBytes; 946 }); 947 } 948 949 return m_sendBytes; 950 } 951 952 size_t ResourceHandleCurlDelegate::didReceiveHeaderCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data) 953 { 954 return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveHeader(String(static_cast<const char*>(ptr), blockSize * numberOfBlocks)); 955 } 956 957 size_t ResourceHandleCurlDelegate::didReceiveDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data) 958 { 959 return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveData(ThreadSafeDataBuffer::copyData(static_cast<const char*>(ptr), blockSize * numberOfBlocks)); 960 } 961 962 size_t ResourceHandleCurlDelegate::willSendDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data) 963 { 964 return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->willSendData(ptr, blockSize, numberOfBlocks); 1064 965 } 1065 966
Note: See TracChangeset
for help on using the changeset viewer.