[Curl] Change member variable of CurlHandle to use std::unique_ptr
[WebKit-https.git] / Source / WebCore / platform / network / curl / ResourceHandleCurlDelegate.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Inc.  All rights reserved.
3  * Copyright (C) 2005, 2006 Michael Emmel mike.emmel@gmail.com
4  * Copyright (C) 2017 Sony Interactive Entertainment Inc.
5  * All rights reserved.
6  * Copyright (C) 2017 NAVER Corp. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "ResourceHandleCurlDelegate.h"
32
33 #if USE(CURL)
34
35 #include "CredentialStorage.h"
36 #include "CurlCacheManager.h"
37 #include "HTTPParsers.h"
38 #include "MIMETypeRegistry.h"
39 #include "MultipartHandle.h"
40 #include "ResourceHandle.h"
41 #include "ResourceHandleInternal.h"
42 #include "SharedBuffer.h"
43 #include "TextEncoding.h"
44 #include "URL.h"
45 #include <wtf/MainThread.h>
46 #include <wtf/text/Base64.h>
47
48 namespace WebCore {
49
50 ResourceHandleCurlDelegate::ResourceHandleCurlDelegate(ResourceHandle* handle)
51     : m_handle(handle)
52     , m_firstRequest(handle->firstRequest().isolatedCopy())
53     , m_customHTTPHeaderFields(m_firstRequest.httpHeaderFields().isolatedCopy())
54     , m_shouldUseCredentialStorage(handle->shouldUseCredentialStorage())
55     , m_user(handle->getInternal()->m_user.isolatedCopy())
56     , m_pass(handle->getInternal()->m_pass.isolatedCopy())
57     , m_initialCredential(handle->getInternal()->m_initialCredential)
58     , m_defersLoading(handle->getInternal()->m_defersLoading)
59 {
60     const URL& url = m_firstRequest.url();
61
62     if (m_customHTTPHeaderFields.size()) {
63         auto& cache = CurlCacheManager::getInstance();
64         bool hasCacheHeaders = m_customHTTPHeaderFields.contains(HTTPHeaderName::IfModifiedSince) || m_customHTTPHeaderFields.contains(HTTPHeaderName::IfNoneMatch);
65         if (!hasCacheHeaders && cache.isCached(url)) {
66             cache.addCacheEntryClient(url, m_handle);
67             // append additional cache information
68             for (auto entry : cache.requestHeaders(url))
69                 m_customHTTPHeaderFields.set(entry.key, entry.value);
70             m_addedCacheValidationHeaders = true;
71         }
72     }
73
74     setupAuthentication();
75 }
76
77 ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate()
78 {
79 }
80
81 bool ResourceHandleCurlDelegate::hasHandle() const
82 {
83     return !!m_handle;
84 }
85
86 void ResourceHandleCurlDelegate::releaseHandle()
87 {
88     m_handle = nullptr;
89 }
90
91 void ResourceHandleCurlDelegate::start(bool isSyncRequest)
92 {
93     m_isSyncRequest = isSyncRequest;
94
95     if (!m_isSyncRequest) {
96         // For asynchronous, use CurlJobManager. Curl processes runs on sub thread.
97         CurlJobManager::singleton().add(this);
98     } else {
99         // For synchronous, does not use CurlJobManager. Curl processes runs on main thread.
100         retain();
101         setupTransfer();
102
103         // curl_easy_perform blocks until the transfer is finished.
104         CURLcode resultCode = m_curlHandle->perform();
105         didCompleteTransfer(resultCode);
106         release();
107     }
108 }
109
110 void ResourceHandleCurlDelegate::cancel()
111 {
112     releaseHandle();
113
114     if (!m_isSyncRequest)
115         CurlJobManager::singleton().cancel(this);
116 }
117
118 void ResourceHandleCurlDelegate::setDefersLoading(bool defers)
119 {
120     if (defers == m_defersLoading)
121         return;
122
123     m_defersLoading = defers;
124
125     auto action = [protectedThis = makeRef(*this)]() {
126         if (!protectedThis->m_curlHandle)
127             return;
128
129         if (protectedThis->m_defersLoading) {
130             CURLcode error = protectedThis->m_curlHandle->pause(CURLPAUSE_ALL);
131             // If we could not defer the handle, so don't do it.
132             if (error != CURLE_OK)
133                 return;
134         } else {
135             CURLcode error = protectedThis->m_curlHandle->pause(CURLPAUSE_CONT);
136             if (error != CURLE_OK) {
137                 // Restarting the handle has failed so just cancel it.
138                 protectedThis->m_handle->cancel();
139             }
140         }
141     };
142
143     CurlJobManager::singleton().callOnJobThread(WTFMove(action));
144 }
145
146 void ResourceHandleCurlDelegate::setAuthentication(const String& user, const String& pass)
147 {
148     auto action = [protectedThis = makeRef(*this), user = user.isolatedCopy(), pass = pass.isolatedCopy()]() {
149         protectedThis->m_user = user;
150         protectedThis->m_pass = pass;
151         if (protectedThis->m_curlHandle)
152             protectedThis->m_curlHandle->setHttpAuthUserPass(user, pass);
153     };
154
155     CurlJobManager::singleton().callOnJobThread(WTFMove(action));
156 }
157
158 void ResourceHandleCurlDelegate::dispatchSynchronousJob()
159 {
160     if (m_firstRequest.url().protocolIsData()) {
161         handleDataURL();
162         return;
163     }
164
165     // If defersLoading is true and we call curl_easy_perform
166     // on a paused handle, libcURL would do the transfert anyway
167     // and we would assert so force defersLoading to be false.
168     m_defersLoading = false;
169
170     start(true);
171 }
172
173 void ResourceHandleCurlDelegate::retain()
174 {
175     ref();
176 }
177
178 void ResourceHandleCurlDelegate::release()
179 {
180     deref();
181 }
182
183 CURL* ResourceHandleCurlDelegate::setupTransfer()
184 {
185     m_curlHandle = std::make_unique<CurlHandle>();
186     m_curlHandle->initialize();
187
188     if (m_defersLoading) {
189         CURLcode error = m_curlHandle->pause(CURLPAUSE_ALL);
190         // If we did not pause the handle, we would ASSERT in the
191         // header callback. So just assert here.
192         ASSERT_UNUSED(error, error == CURLE_OK);
193     }
194
195 #ifndef NDEBUG
196     m_curlHandle->enableVerboseIfUsed();
197     m_curlHandle->enableStdErrIfUsed();
198 #endif
199
200     auto& sslHandle = CurlContext::singleton().sslHandle();
201
202     m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Enable);
203     m_curlHandle->setSslVerifyHost(CurlHandle::VerifyHost::StrictNameCheck);
204     m_curlHandle->setWriteCallbackFunction(didReceiveDataCallback, this);
205     m_curlHandle->setHeaderCallbackFunction(didReceiveHeaderCallback, this);
206     m_curlHandle->enableAutoReferer();
207     m_curlHandle->enableFollowLocation();
208     m_curlHandle->enableHttpAuthentication(CURLAUTH_ANY);
209     m_curlHandle->enableShareHandle();
210     m_curlHandle->enableTimeout();
211     m_curlHandle->enableAllowedProtocols();
212
213     auto sslClientCertificate = sslHandle.getSSLClientCertificate(m_firstRequest.url().host());
214     if (sslClientCertificate) {
215         m_curlHandle->setSslCert(sslClientCertificate->first.utf8().data());
216         m_curlHandle->setSslCertType("P12");
217         m_curlHandle->setSslKeyPassword(sslClientCertificate->second.utf8().data());
218     }
219
220     if (sslHandle.shouldIgnoreSSLErrors())
221         m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Disable);
222     else
223         m_curlHandle->setSslCtxCallbackFunction(willSetupSslCtxCallback, this);
224
225     m_curlHandle->setCACertPath(sslHandle.getCACertPath());
226
227     m_curlHandle->enableAcceptEncoding();
228     m_curlHandle->setUrl(m_firstRequest.url());
229     m_curlHandle->enableCookieJarIfExists();
230
231     if (m_customHTTPHeaderFields.size())
232         m_curlHandle->appendRequestHeaders(m_customHTTPHeaderFields);
233
234     String method = m_firstRequest.httpMethod();
235     if ("GET" == method)
236         m_curlHandle->enableHttpGetRequest();
237     else if ("POST" == method)
238         setupPOST();
239     else if ("PUT" == method)
240         setupPUT();
241     else if ("HEAD" == method)
242         m_curlHandle->enableHttpHeadRequest();
243     else {
244         m_curlHandle->setHttpCustomRequest(method);
245         setupPUT();
246     }
247
248     applyAuthentication();
249
250     m_curlHandle->enableProxyIfExists();
251
252     return m_curlHandle->handle();
253 }
254
255 void ResourceHandleCurlDelegate::didCompleteTransfer(CURLcode result)
256 {
257     if (result == CURLE_OK) {
258         NetworkLoadMetrics networkLoadMetrics = getNetworkLoadMetrics();
259
260         if (isMainThread())
261             didFinish(networkLoadMetrics);
262         else {
263             callOnMainThread([protectedThis = makeRef(*this), metrics = networkLoadMetrics.isolatedCopy()] {
264                 if (!protectedThis->m_handle)
265                     return;
266                 protectedThis->didFinish(metrics);
267             });
268         }
269     } else {
270         ResourceError resourceError = ResourceError::httpError(result, m_firstRequest.url());
271         if (m_sslVerifier.sslErrors())
272             resourceError.setSslErrors(m_sslVerifier.sslErrors());
273
274         if (isMainThread())
275             didFail(resourceError);
276         else {
277             callOnMainThread([protectedThis = makeRef(*this), error = resourceError.isolatedCopy()] {
278                 if (!protectedThis->m_handle)
279                     return;
280                 protectedThis->didFail(error);
281             });
282         }
283     }
284
285     m_formDataStream = nullptr;
286     m_curlHandle = nullptr;
287 }
288
289 void ResourceHandleCurlDelegate::didCancelTransfer()
290 {
291     m_formDataStream = nullptr;
292     m_curlHandle = nullptr;
293 }
294
295 ResourceResponse& ResourceHandleCurlDelegate::response()
296 {
297     return m_handle->getInternal()->m_response;
298 }
299
300 void ResourceHandleCurlDelegate::setupAuthentication()
301 {
302     // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
303     String partition = m_firstRequest.cachePartition();
304
305     if (m_shouldUseCredentialStorage) {
306         if (m_user.isEmpty() && m_pass.isEmpty()) {
307             // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
308             // try and reuse the credential preemptively, as allowed by RFC 2617.
309             m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, m_firstRequest.url());
310         } else {
311             // If there is already a protection space known for the URL, update stored credentials
312             // before sending a request. This makes it possible to implement logout by sending an
313             // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
314             // an authentication dialog doesn't pop up).
315             CredentialStorage::defaultCredentialStorage().set(partition, Credential(m_user, m_pass, CredentialPersistenceNone), m_firstRequest.url());
316         }
317     }
318 }
319
320 inline static bool isHttpInfo(int statusCode)
321 {
322     return 100 <= statusCode && statusCode < 200;
323 }
324
325 void ResourceHandleCurlDelegate::didReceiveAllHeaders(URL url, long httpCode, long long contentLength, uint16_t connectPort, long availableHttpAuth)
326 {
327     ASSERT(isMainThread());
328
329     response().setURL(url);
330     response().setExpectedContentLength(contentLength);
331     response().setHTTPStatusCode(httpCode);
332     response().setMimeType(extractMIMETypeFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
333     response().setTextEncodingName(extractCharsetFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)));
334
335     if (response().isMultipart()) {
336         String boundary;
337         bool parsed = MultipartHandle::extractBoundary(response().httpHeaderField(HTTPHeaderName::ContentType), boundary);
338         if (parsed)
339             m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary);
340     }
341
342     // HTTP redirection
343     if (response().isRedirection()) {
344         String location = response().httpHeaderField(HTTPHeaderName::Location);
345         if (!location.isEmpty()) {
346             URL newURL = URL(m_firstRequest.url(), location);
347
348             ResourceRequest redirectedRequest = m_firstRequest;
349             redirectedRequest.setURL(newURL);
350             ResourceResponse localResponse = response();
351             if (m_handle->client())
352                 m_handle->client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(localResponse));
353
354             m_firstRequest.setURL(newURL);
355
356             return;
357         }
358     } else if (response().isUnauthorized()) {
359         AuthenticationChallenge challenge(connectPort, availableHttpAuth, m_authFailureCount, response(), m_handle);
360         m_handle->didReceiveAuthenticationChallenge(challenge);
361         m_authFailureCount++;
362         return;
363     }
364
365     response().setResponseFired(true);
366
367     if (m_handle->client()) {
368         if (response().isNotModified()) {
369             const String& url = m_firstRequest.url().string();
370             if (CurlCacheManager::getInstance().getCachedResponse(url, response())) {
371                 if (m_addedCacheValidationHeaders) {
372                     response().setHTTPStatusCode(200);
373                     response().setHTTPStatusText("OK");
374                 }
375             }
376         }
377         CurlCacheManager::getInstance().didReceiveResponse(*m_handle, response());
378         m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
379     }
380 }
381
382 void ResourceHandleCurlDelegate::didReceiveContentData(Ref<SharedBuffer>&& buffer)
383 {
384     ASSERT(isMainThread());
385
386     if (!response().responseFired())
387         handleLocalReceiveResponse();
388
389     if (m_multipartHandle)
390         m_multipartHandle->contentReceived(buffer->data(), buffer->size());
391     else if (m_handle->client()) {
392         CurlCacheManager::getInstance().didReceiveData(*m_handle, buffer->data(), buffer->size());
393         m_handle->client()->didReceiveBuffer(m_handle, WTFMove(buffer), buffer->size());
394     }
395 }
396
397 void ResourceHandleCurlDelegate::handleLocalReceiveResponse()
398 {
399     ASSERT(isMainThread());
400
401     // since the code in headerCallback will not have run for local files
402     // the code to set the URL and fire didReceiveResponse is never run,
403     // which means the ResourceLoader's response does not contain the URL.
404     // Run the code here for local files to resolve the issue.
405     // TODO: See if there is a better approach for handling this.
406     URL url = m_curlHandle->getEffectiveURL();
407     ASSERT(url.isValid());
408     response().setURL(url);
409
410     // Determine the MIME type based on the path.
411     response().setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
412     response().setResponseFired(true);
413     if (m_handle->client())
414         m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
415 }
416
417 void ResourceHandleCurlDelegate::prepareSendData(char* buffer, size_t blockSize, size_t numberOfBlocks)
418 {
419     ASSERT(isMainThread());
420     ASSERT(!m_sendBytes);
421
422     std::unique_lock<Lock> lock(m_workerThreadMutex);
423
424     if (!m_formDataStream || !m_formDataStream->hasMoreElements()) {
425         m_workerThreadConditionVariable.notifyOne();
426         return;
427     }
428
429     size_t size = m_formDataStream->read(buffer, blockSize, numberOfBlocks);
430     if (!size) {
431         // Something went wrong so cancel the job.
432         m_handle->cancel();
433         m_workerThreadConditionVariable.notifyOne();
434         return;
435     }
436
437     m_sendBytes = size;
438     m_workerThreadConditionVariable.notifyOne();
439 }
440
441 void ResourceHandleCurlDelegate::didFinish(NetworkLoadMetrics networkLoadMetrics)
442 {
443     response().setDeprecatedNetworkLoadMetrics(networkLoadMetrics);
444
445     if (!m_handle)
446         return;
447
448     if (!response().responseFired()) {
449         handleLocalReceiveResponse();
450         if (!m_handle)
451             return;
452     }
453
454     if (m_multipartHandle)
455         m_multipartHandle->contentEnded();
456
457     if (m_handle->client()) {
458         CurlCacheManager::getInstance().didFinishLoading(*m_handle);
459         m_handle->client()->didFinishLoading(m_handle);
460     }
461 }
462
463 void ResourceHandleCurlDelegate::didFail(const ResourceError& resourceError)
464 {
465     if (!m_handle)
466         return;
467
468     if (m_handle->client()) {
469         CurlCacheManager::getInstance().didFail(*m_handle);
470         m_handle->client()->didFail(m_handle, resourceError);
471     }
472 }
473
474 void ResourceHandleCurlDelegate::handleDataURL()
475 {
476     ASSERT(m_firstRequest.url().protocolIsData());
477     String url = m_firstRequest.url().string();
478
479     ASSERT(m_handle->client());
480
481     int index = url.find(',');
482     if (index == -1) {
483         m_handle->client()->cannotShowURL(m_handle);
484         return;
485     }
486
487     String mediaType = url.substring(5, index - 5);
488     String data = url.substring(index + 1);
489     auto originalSize = data.length();
490
491     bool base64 = mediaType.endsWith(";base64", false);
492     if (base64)
493         mediaType = mediaType.left(mediaType.length() - 7);
494
495     if (mediaType.isEmpty())
496         mediaType = "text/plain";
497
498     String mimeType = extractMIMETypeFromMediaType(mediaType);
499     String charset = extractCharsetFromMediaType(mediaType);
500
501     if (charset.isEmpty())
502         charset = "US-ASCII";
503
504     ResourceResponse response;
505     response.setMimeType(mimeType);
506     response.setTextEncodingName(charset);
507     response.setURL(m_firstRequest.url());
508
509     if (base64) {
510         data = decodeURLEscapeSequences(data);
511         m_handle->client()->didReceiveResponse(m_handle, WTFMove(response));
512
513         // didReceiveResponse might cause the client to be deleted.
514         if (m_handle->client()) {
515             Vector<char> out;
516             if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
517                 m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::create(out.data(), out.size()), originalSize);
518         }
519     } else {
520         TextEncoding encoding(charset);
521         data = decodeURLEscapeSequences(data, encoding);
522         m_handle->client()->didReceiveResponse(m_handle, WTFMove(response));
523
524         // didReceiveResponse might cause the client to be deleted.
525         if (m_handle->client()) {
526             CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
527             if (encodedData.length())
528                 m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::create(encodedData.data(), encodedData.length()), originalSize);
529         }
530     }
531
532     if (m_handle->client())
533         m_handle->client()->didFinishLoading(m_handle);
534 }
535
536 void ResourceHandleCurlDelegate::setupPOST()
537 {
538     m_curlHandle->enableHttpPostRequest();
539
540     size_t numElements = getFormElementsCount();
541     if (!numElements)
542         return;
543
544     // Do not stream for simple POST data
545     if (numElements == 1) {
546         m_postBytes = m_firstRequest.httpBody()->flatten();
547         if (m_postBytes.size())
548             m_curlHandle->setPostFields(m_postBytes.data(), m_postBytes.size());
549         return;
550     }
551
552     setupFormData(true);
553 }
554
555 void ResourceHandleCurlDelegate::setupPUT()
556 {
557     m_curlHandle->enableHttpPutRequest();
558
559     // Disable the Expect: 100 continue header
560     m_curlHandle->removeRequestHeader("Expect");
561
562     size_t numElements = getFormElementsCount();
563     if (!numElements)
564         return;
565
566     setupFormData(false);
567 }
568
569 size_t ResourceHandleCurlDelegate::getFormElementsCount()
570 {
571     RefPtr<FormData> formData = m_firstRequest.httpBody();
572     if (!formData)
573         return 0;
574
575     // Resolve the blob elements so the formData can correctly report it's size.
576     formData = formData->resolveBlobReferences();
577     size_t size = formData->elements().size();
578     m_firstRequest.setHTTPBody(WTFMove(formData));
579     return size;
580 }
581
582 void ResourceHandleCurlDelegate::setupFormData(bool isPostRequest)
583 {
584     Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements();
585     size_t numElements = elements.size();
586
587     static const long long maxCurlOffT = m_curlHandle->maxCurlOffT();
588
589     // Obtain the total size of the form data
590     curl_off_t size = 0;
591     bool chunkedTransfer = false;
592     for (size_t i = 0; i < numElements; i++) {
593         FormDataElement element = elements[i];
594         if (element.m_type == FormDataElement::Type::EncodedFile) {
595             long long fileSizeResult;
596             if (getFileSize(element.m_filename, fileSizeResult)) {
597                 if (fileSizeResult > maxCurlOffT) {
598                     // File size is too big for specifying it to cURL
599                     chunkedTransfer = true;
600                     break;
601                 }
602                 size += fileSizeResult;
603             } else {
604                 chunkedTransfer = true;
605                 break;
606             }
607         } else
608             size += elements[i].m_data.size();
609     }
610
611     // cURL guesses that we want chunked encoding as long as we specify the header
612     if (chunkedTransfer)
613         m_curlHandle->appendRequestHeader("Transfer-Encoding: chunked");
614     else {
615         if (isPostRequest)
616             m_curlHandle->setPostFieldLarge(size);
617         else
618             m_curlHandle->setInFileSizeLarge(size);
619     }
620
621     m_formDataStream = std::make_unique<FormDataStream>();
622     m_formDataStream->setHTTPBody(m_firstRequest.httpBody());
623
624     m_curlHandle->setReadCallbackFunction(willSendDataCallback, this);
625 }
626
627 void ResourceHandleCurlDelegate::applyAuthentication()
628 {
629     String user = m_user;
630     String password = m_pass;
631
632     if (!m_initialCredential.isEmpty()) {
633         user = m_initialCredential.user();
634         password = m_initialCredential.password();
635         m_curlHandle->enableHttpAuthentication(CURLAUTH_BASIC);
636     }
637
638     // It seems we need to set CURLOPT_USERPWD even if username and password is empty.
639     // Otherwise cURL will not automatically continue with a new request after a 401 response.
640
641     // curl CURLOPT_USERPWD expects username:password
642     m_curlHandle->setHttpAuthUserPass(user, password);
643 }
644
645 NetworkLoadMetrics ResourceHandleCurlDelegate::getNetworkLoadMetrics()
646 {
647     NetworkLoadMetrics networkLoadMetrics;
648     if (auto metrics = m_curlHandle->getNetworkLoadMetrics())
649         networkLoadMetrics = *metrics;
650
651     return networkLoadMetrics;
652 }
653
654 CURLcode ResourceHandleCurlDelegate::willSetupSslCtx(void* sslCtx)
655 {
656     m_sslVerifier.setCurlHandle(m_curlHandle.get());
657     m_sslVerifier.setHostName(m_firstRequest.url().host());
658     m_sslVerifier.setSslCtx(sslCtx);
659
660     return CURLE_OK;
661 }
662
663 /*
664 * This is being called for each HTTP header in the response. This includes '\r\n'
665 * for the last line of the header.
666 *
667 * We will add each HTTP Header to the ResourceResponse and on the termination
668 * of the header (\r\n) we will parse Content-Type and Content-Disposition and
669 * update the ResourceResponse and then send it away.
670 *
671 */
672 size_t ResourceHandleCurlDelegate::didReceiveHeader(String&& header)
673 {
674     if (!m_handle)
675         return 0;
676
677     if (m_defersLoading)
678         return 0;
679
680     /*
681     * a) We can finish and send the ResourceResponse
682     * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse
683     *
684     * The HTTP standard requires to use \r\n but for compatibility it recommends to
685     * accept also \n.
686     */
687     if (header == AtomicString("\r\n") || header == AtomicString("\n")) {
688         long httpCode = 0;
689         if (auto code = m_curlHandle->getResponseCode())
690             httpCode = *code;
691
692         if (!httpCode) {
693             // Comes here when receiving 200 Connection Established. Just return.
694             return header.length();
695         }
696
697         if (isHttpInfo(httpCode)) {
698             // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.
699             // If not, the request might be cancelled, because the MIME type will be empty for this response.
700             return header.length();
701         }
702
703         URL url = m_curlHandle->getEffectiveURL();
704
705         long long contentLength = 0;
706         if (auto length = m_curlHandle->getContentLength())
707             contentLength = *length;
708
709         uint16_t connectPort = 0;
710         if (auto port = m_curlHandle->getPrimaryPort())
711             connectPort = *port;
712
713         long availableAuth = CURLAUTH_NONE;
714         if (auto auth = m_curlHandle->getHttpAuthAvail())
715             availableAuth = *auth;
716
717         if (isMainThread())
718             didReceiveAllHeaders(url, httpCode, contentLength, connectPort, availableAuth);
719         else {
720             callOnMainThread([protectedThis = makeRef(*this), copyUrl = url.isolatedCopy(), httpCode, contentLength, connectPort, availableAuth] {
721                 if (!protectedThis->m_handle)
722                     return;
723                 protectedThis->didReceiveAllHeaders(copyUrl, httpCode, contentLength, connectPort, availableAuth);
724             });
725         }
726     } else {
727         // If the FOLLOWLOCATION option is enabled for the curl handle then
728         // curl will follow the redirections internally. Thus this header callback
729         // will be called more than one time with the line starting "HTTP" for one job.
730         if (isMainThread())
731             response().appendHTTPHeaderField(header);
732         else {
733             callOnMainThread([protectedThis = makeRef(*this), copyHeader = header.isolatedCopy() ] {
734                 if (!protectedThis->m_handle)
735                     return;
736
737                 protectedThis->response().appendHTTPHeaderField(copyHeader);
738             });
739         }
740     }
741
742     return header.length();
743 }
744
745 // called with data after all headers have been processed via headerCallback
746 size_t ResourceHandleCurlDelegate::didReceiveData(Ref<SharedBuffer>&& buffer)
747 {
748     if (!m_handle)
749         return 0;
750
751     if (m_defersLoading)
752         return 0;
753
754     size_t receiveBytes = buffer->size();
755
756     // this shouldn't be necessary but apparently is. CURL writes the data
757     // of html page even if it is a redirect that was handled internally
758     // can be observed e.g. on gmail.com
759     if (auto httpCode = m_curlHandle->getResponseCode()) {
760         if (*httpCode >= 300 && *httpCode < 400)
761             return receiveBytes;
762     }
763
764     if (receiveBytes) {
765         if (isMainThread())
766             didReceiveContentData(WTFMove(buffer));
767         else {
768             callOnMainThread([protectedThis = makeRef(*this), buf = WTFMove(buffer)]() mutable {
769                 if (!protectedThis->m_handle)
770                     return;
771                 protectedThis->didReceiveContentData(WTFMove(buf));
772             });
773         }
774     }
775
776     return receiveBytes;
777 }
778
779 /* This is called to obtain HTTP POST or PUT data.
780 Iterate through FormData elements and upload files.
781 Carefully respect the given buffer blockSize and fill the rest of the data at the next calls.
782 */
783 size_t ResourceHandleCurlDelegate::willSendData(char* buffer, size_t blockSize, size_t numberOfBlocks)
784 {
785     ASSERT(!isMainThread());
786
787     if (!m_handle)
788         return 0;
789
790     if (m_defersLoading)
791         return 0;
792
793     if (!blockSize || !numberOfBlocks)
794         return 0;
795
796     {
797         std::unique_lock<Lock> lock(m_workerThreadMutex);
798
799         m_sendBytes = 0;
800
801         if (isMainThread())
802             prepareSendData(buffer, blockSize, numberOfBlocks);
803         else {
804             callOnMainThread([protectedThis = makeRef(*this), buffer, blockSize, numberOfBlocks] {
805                 if (!protectedThis->m_handle)
806                     return;
807                 protectedThis->prepareSendData(buffer, blockSize, numberOfBlocks);
808             });
809
810             m_workerThreadConditionVariable.wait(lock, [this] {
811                 return m_sendBytes;
812             });
813         }
814     }
815
816     return m_sendBytes;
817 }
818
819 CURLcode ResourceHandleCurlDelegate::willSetupSslCtxCallback(CURL*, void* sslCtx, void* userData)
820 {
821     return static_cast<ResourceHandleCurlDelegate*>(userData)->willSetupSslCtx(sslCtx);
822 }
823
824 size_t ResourceHandleCurlDelegate::didReceiveHeaderCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
825 {
826     return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveHeader(String(static_cast<const char*>(ptr), blockSize * numberOfBlocks));
827 }
828
829 size_t ResourceHandleCurlDelegate::didReceiveDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
830 {
831     return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveData(SharedBuffer::create(ptr, blockSize * numberOfBlocks));
832 }
833
834 size_t ResourceHandleCurlDelegate::willSendDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
835 {
836     return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->willSendData(ptr, blockSize, numberOfBlocks);
837 }
838
839 } // namespace WebCore
840
841 #endif