• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
4  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5  * Copyright (C) 2007 Holger Hans Peter Freyther
6  * Copyright (C) 2008 Collabora Ltd.
7  * Copyright (C) 2008 Nuanti Ltd.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 #include "ResourceHandleManager.h"
34 
35 #include "Base64.h"
36 #include "CString.h"
37 #include "HTTPParsers.h"
38 #include "MIMETypeRegistry.h"
39 #include "NotImplemented.h"
40 #include "ResourceError.h"
41 #include "ResourceHandle.h"
42 #include "ResourceHandleInternal.h"
43 #include "TextEncoding.h"
44 
45 #include <errno.h>
46 #include <stdio.h>
47 #include <wtf/Vector.h>
48 
49 namespace WebCore {
50 
51 const int selectTimeoutMS = 5;
52 const double pollTimeSeconds = 0.05;
53 const int maxRunningJobs = 5;
54 
55 static const bool ignoreSSLErrors = getenv("WEBKIT_IGNORE_SSL_ERRORS");
56 
ResourceHandleManager()57 ResourceHandleManager::ResourceHandleManager()
58     : m_downloadTimer(this, &ResourceHandleManager::downloadTimerCallback)
59     , m_cookieJarFileName(0)
60     , m_runningJobs(0)
61 {
62     curl_global_init(CURL_GLOBAL_ALL);
63     m_curlMultiHandle = curl_multi_init();
64     m_curlShareHandle = curl_share_init();
65     curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
66     curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
67 }
68 
~ResourceHandleManager()69 ResourceHandleManager::~ResourceHandleManager()
70 {
71     curl_multi_cleanup(m_curlMultiHandle);
72     curl_share_cleanup(m_curlShareHandle);
73     if (m_cookieJarFileName)
74         free(m_cookieJarFileName);
75     curl_global_cleanup();
76 }
77 
setCookieJarFileName(const char * cookieJarFileName)78 void ResourceHandleManager::setCookieJarFileName(const char* cookieJarFileName)
79 {
80     m_cookieJarFileName = strdup(cookieJarFileName);
81 }
82 
sharedInstance()83 ResourceHandleManager* ResourceHandleManager::sharedInstance()
84 {
85     static ResourceHandleManager* sharedInstance = 0;
86     if (!sharedInstance)
87         sharedInstance = new ResourceHandleManager();
88     return sharedInstance;
89 }
90 
91 // called with data after all headers have been processed via headerCallback
writeCallback(void * ptr,size_t size,size_t nmemb,void * data)92 static size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* data)
93 {
94     ResourceHandle* job = static_cast<ResourceHandle*>(data);
95     ResourceHandleInternal* d = job->getInternal();
96     if (d->m_cancelled)
97         return 0;
98 
99 #if LIBCURL_VERSION_NUM > 0x071200
100     // We should never be called when deferred loading is activated.
101     ASSERT(!d->m_defersLoading);
102 #endif
103 
104     size_t totalSize = size * nmemb;
105 
106     // this shouldn't be necessary but apparently is. CURL writes the data
107     // of html page even if it is a redirect that was handled internally
108     // can be observed e.g. on gmail.com
109     CURL* h = d->m_handle;
110     long httpCode = 0;
111     CURLcode err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode);
112     if (CURLE_OK == err && httpCode >= 300 && httpCode < 400)
113         return totalSize;
114 
115     // since the code in headerCallback will not have run for local files
116     // the code to set the URL and fire didReceiveResponse is never run,
117     // which means the ResourceLoader's response does not contain the URL.
118     // Run the code here for local files to resolve the issue.
119     // TODO: See if there is a better approach for handling this.
120     if (!d->m_response.responseFired()) {
121         const char* hdr;
122         err = curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &hdr);
123         d->m_response.setURL(KURL(hdr));
124         if (d->client())
125             d->client()->didReceiveResponse(job, d->m_response);
126         d->m_response.setResponseFired(true);
127     }
128 
129     if (d->client())
130         d->client()->didReceiveData(job, static_cast<char*>(ptr), totalSize, 0);
131     return totalSize;
132 }
133 
134 /*
135  * This is being called for each HTTP header in the response. This includes '\r\n'
136  * for the last line of the header.
137  *
138  * We will add each HTTP Header to the ResourceResponse and on the termination
139  * of the header (\r\n) we will parse Content-Type and Content-Disposition and
140  * update the ResourceResponse and then send it away.
141  *
142  */
headerCallback(char * ptr,size_t size,size_t nmemb,void * data)143 static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
144 {
145     ResourceHandle* job = static_cast<ResourceHandle*>(data);
146     ResourceHandleInternal* d = job->getInternal();
147     if (d->m_cancelled)
148         return 0;
149 
150 #if LIBCURL_VERSION_NUM > 0x071200
151     // We should never be called when deferred loading is activated.
152     ASSERT(!d->m_defersLoading);
153 #endif
154 
155     size_t totalSize = size * nmemb;
156     ResourceHandleClient* client = d->client();
157 
158     String header(static_cast<const char*>(ptr), totalSize);
159 
160     /*
161      * a) We can finish and send the ResourceResponse
162      * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse
163      *
164      * The HTTP standard requires to use \r\n but for compatibility it recommends to
165      * accept also \n.
166      */
167     if (header == String("\r\n") || header == String("\n")) {
168         CURL* h = d->m_handle;
169         CURLcode err;
170 
171         double contentLength = 0;
172         err = curl_easy_getinfo(h, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
173         d->m_response.setExpectedContentLength(static_cast<long long int>(contentLength));
174 
175         const char* hdr;
176         err = curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &hdr);
177         d->m_response.setURL(KURL(hdr));
178 
179         long httpCode = 0;
180         err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode);
181         d->m_response.setHTTPStatusCode(httpCode);
182 
183         d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField("Content-Type")));
184         d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField("Content-Type")));
185         d->m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(d->m_response.httpHeaderField("Content-Disposition")));
186 
187         // HTTP redirection
188         if (httpCode >= 300 && httpCode < 400) {
189             String location = d->m_response.httpHeaderField("location");
190             if (!location.isEmpty()) {
191                 KURL newURL = KURL(job->request().url(), location);
192 
193                 ResourceRequest redirectedRequest = job->request();
194                 redirectedRequest.setURL(newURL);
195                 if (client)
196                     client->willSendRequest(job, redirectedRequest, d->m_response);
197 
198                 d->m_request.setURL(newURL);
199 
200                 return totalSize;
201             }
202         }
203 
204         if (client)
205             client->didReceiveResponse(job, d->m_response);
206         d->m_response.setResponseFired(true);
207 
208     } else {
209         int splitPos = header.find(":");
210         if (splitPos != -1)
211             d->m_response.setHTTPHeaderField(header.left(splitPos), header.substring(splitPos+1).stripWhiteSpace());
212     }
213 
214     return totalSize;
215 }
216 
217 /* This is called to obtain HTTP POST or PUT data.
218    Iterate through FormData elements and upload files.
219    Carefully respect the given buffer size and fill the rest of the data at the next calls.
220 */
readCallback(void * ptr,size_t size,size_t nmemb,void * data)221 size_t readCallback(void* ptr, size_t size, size_t nmemb, void* data)
222 {
223     ResourceHandle* job = static_cast<ResourceHandle*>(data);
224     ResourceHandleInternal* d = job->getInternal();
225     if (d->m_cancelled)
226         return 0;
227 
228 #if LIBCURL_VERSION_NUM > 0x071200
229     // We should never be called when deferred loading is activated.
230     ASSERT(!d->m_defersLoading);
231 #endif
232 
233     if (!size || !nmemb)
234         return 0;
235 
236     if (!d->m_formDataStream.hasMoreElements())
237         return 0;
238 
239     size_t sent = d->m_formDataStream.read(ptr, size, nmemb);
240 
241     // Something went wrong so cancel the job.
242     if (!sent)
243         job->cancel();
244 
245     return sent;
246 }
247 
downloadTimerCallback(Timer<ResourceHandleManager> * timer)248 void ResourceHandleManager::downloadTimerCallback(Timer<ResourceHandleManager>* timer)
249 {
250     startScheduledJobs();
251 
252     fd_set fdread;
253     fd_set fdwrite;
254     fd_set fdexcep;
255     int maxfd = 0;
256 
257     struct timeval timeout;
258     timeout.tv_sec = 0;
259     timeout.tv_usec = selectTimeoutMS * 1000;       // select waits microseconds
260 
261     // Retry 'select' if it was interrupted by a process signal.
262     int rc = 0;
263     do {
264         FD_ZERO(&fdread);
265         FD_ZERO(&fdwrite);
266         FD_ZERO(&fdexcep);
267         curl_multi_fdset(m_curlMultiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
268         // When the 3 file descriptors are empty, winsock will return -1
269         // and bail out, stopping the file download. So make sure we
270         // have valid file descriptors before calling select.
271         if (maxfd >= 0)
272             rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
273     } while (rc == -1 && errno == EINTR);
274 
275     if (-1 == rc) {
276 #ifndef NDEBUG
277         perror("bad: select() returned -1: ");
278 #endif
279         return;
280     }
281 
282     int runningHandles = 0;
283     while (curl_multi_perform(m_curlMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) { }
284 
285     // check the curl messages indicating completed transfers
286     // and free their resources
287     while (true) {
288         int messagesInQueue;
289         CURLMsg* msg = curl_multi_info_read(m_curlMultiHandle, &messagesInQueue);
290         if (!msg)
291             break;
292 
293         // find the node which has same d->m_handle as completed transfer
294         CURL* handle = msg->easy_handle;
295         ASSERT(handle);
296         ResourceHandle* job = 0;
297         CURLcode err = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job);
298         ASSERT(CURLE_OK == err);
299         ASSERT(job);
300         if (!job)
301             continue;
302         ResourceHandleInternal* d = job->getInternal();
303         ASSERT(d->m_handle == handle);
304 
305         if (d->m_cancelled) {
306             removeFromCurl(job);
307             continue;
308         }
309 
310         if (CURLMSG_DONE != msg->msg)
311             continue;
312 
313         if (CURLE_OK == msg->data.result) {
314             if (d->client())
315                 d->client()->didFinishLoading(job);
316         } else {
317             char* url = 0;
318             curl_easy_getinfo(d->m_handle, CURLINFO_EFFECTIVE_URL, &url);
319 #ifndef NDEBUG
320             fprintf(stderr, "Curl ERROR for url='%s', error: '%s'\n", url, curl_easy_strerror(msg->data.result));
321 #endif
322             if (d->client())
323                 d->client()->didFail(job, ResourceError(String(), msg->data.result, String(url), String(curl_easy_strerror(msg->data.result))));
324         }
325 
326         removeFromCurl(job);
327     }
328 
329     bool started = startScheduledJobs(); // new jobs might have been added in the meantime
330 
331     if (!m_downloadTimer.isActive() && (started || (runningHandles > 0)))
332         m_downloadTimer.startOneShot(pollTimeSeconds);
333 }
334 
removeFromCurl(ResourceHandle * job)335 void ResourceHandleManager::removeFromCurl(ResourceHandle* job)
336 {
337     ResourceHandleInternal* d = job->getInternal();
338     ASSERT(d->m_handle);
339     if (!d->m_handle)
340         return;
341     m_runningJobs--;
342     curl_multi_remove_handle(m_curlMultiHandle, d->m_handle);
343     curl_easy_cleanup(d->m_handle);
344     d->m_handle = 0;
345     job->deref();
346 }
347 
setupPUT(ResourceHandle *,struct curl_slist **)348 void ResourceHandleManager::setupPUT(ResourceHandle*, struct curl_slist**)
349 {
350     notImplemented();
351 }
352 
353 /* Calculate the length of the POST.
354    Force chunked data transfer if size of files can't be obtained.
355  */
setupPOST(ResourceHandle * job,struct curl_slist ** headers)356 void ResourceHandleManager::setupPOST(ResourceHandle* job, struct curl_slist** headers)
357 {
358     ResourceHandleInternal* d = job->getInternal();
359     curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE);
360     curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, 0);
361 
362     if (!job->request().httpBody())
363         return;
364 
365     Vector<FormDataElement> elements = job->request().httpBody()->elements();
366     size_t numElements = elements.size();
367     if (!numElements)
368         return;
369 
370     // Do not stream for simple POST data
371     if (numElements == 1) {
372         job->request().httpBody()->flatten(d->m_postBytes);
373         if (d->m_postBytes.size() != 0) {
374             curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, d->m_postBytes.size());
375             curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, d->m_postBytes.data());
376         }
377         return;
378     }
379 
380     // Obtain the total size of the POST
381     // The size of a curl_off_t could be different in WebKit and in cURL depending on
382     // compilation flags of both. For CURLOPT_POSTFIELDSIZE_LARGE we have to pass the
383     // right size or random data will be used as the size.
384     static int expectedSizeOfCurlOffT = 0;
385     if (!expectedSizeOfCurlOffT) {
386         curl_version_info_data *infoData = curl_version_info(CURLVERSION_NOW);
387         if (infoData->features & CURL_VERSION_LARGEFILE)
388             expectedSizeOfCurlOffT = sizeof(long long);
389         else
390             expectedSizeOfCurlOffT = sizeof(int);
391     }
392 
393 #if COMPILER(MSVC)
394     // work around compiler error in Visual Studio 2005.  It can't properly
395     // handle math with 64-bit constant declarations.
396 #pragma warning(disable: 4307)
397 #endif
398     static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT * 8 - 1)) - 1;
399     curl_off_t size = 0;
400     bool chunkedTransfer = false;
401     for (size_t i = 0; i < numElements; i++) {
402         FormDataElement element = elements[i];
403         if (element.m_type == FormDataElement::encodedFile) {
404             long long fileSizeResult;
405             if (getFileSize(element.m_filename, fileSizeResult)) {
406                 if (fileSizeResult > maxCurlOffT) {
407                     // File size is too big for specifying it to cURL
408                     chunkedTransfer = true;
409                     break;
410                 }
411                 size += fileSizeResult;
412             } else {
413                 chunkedTransfer = true;
414                 break;
415             }
416         } else
417             size += elements[i].m_data.size();
418     }
419 
420     // cURL guesses that we want chunked encoding as long as we specify the header
421     if (chunkedTransfer)
422         *headers = curl_slist_append(*headers, "Transfer-Encoding: chunked");
423     else {
424         if (sizeof(long long) == expectedSizeOfCurlOffT)
425           curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (long long)size);
426         else
427           curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (int)size);
428     }
429 
430     curl_easy_setopt(d->m_handle, CURLOPT_READFUNCTION, readCallback);
431     curl_easy_setopt(d->m_handle, CURLOPT_READDATA, job);
432 }
433 
add(ResourceHandle * job)434 void ResourceHandleManager::add(ResourceHandle* job)
435 {
436     // we can be called from within curl, so to avoid re-entrancy issues
437     // schedule this job to be added the next time we enter curl download loop
438     job->ref();
439     m_resourceHandleList.append(job);
440     if (!m_downloadTimer.isActive())
441         m_downloadTimer.startOneShot(pollTimeSeconds);
442 }
443 
removeScheduledJob(ResourceHandle * job)444 bool ResourceHandleManager::removeScheduledJob(ResourceHandle* job)
445 {
446     int size = m_resourceHandleList.size();
447     for (int i = 0; i < size; i++) {
448         if (job == m_resourceHandleList[i]) {
449             m_resourceHandleList.remove(i);
450             job->deref();
451             return true;
452         }
453     }
454     return false;
455 }
456 
startScheduledJobs()457 bool ResourceHandleManager::startScheduledJobs()
458 {
459     // TODO: Create a separate stack of jobs for each domain.
460 
461     bool started = false;
462     while (!m_resourceHandleList.isEmpty() && m_runningJobs < maxRunningJobs) {
463         ResourceHandle* job = m_resourceHandleList[0];
464         m_resourceHandleList.remove(0);
465         startJob(job);
466         started = true;
467     }
468     return started;
469 }
470 
parseDataUrl(ResourceHandle * handle)471 static void parseDataUrl(ResourceHandle* handle)
472 {
473     ResourceHandleClient* client = handle->client();
474 
475     ASSERT(client);
476     if (!client)
477         return;
478 
479     String url = handle->request().url().string();
480     ASSERT(url.startsWith("data:", false));
481 
482     int index = url.find(',');
483     if (index == -1) {
484         client->cannotShowURL(handle);
485         return;
486     }
487 
488     String mediaType = url.substring(5, index - 5);
489     String data = url.substring(index + 1);
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;charset=US-ASCII";
497 
498     String mimeType = extractMIMETypeFromMediaType(mediaType);
499     String charset = extractCharsetFromMediaType(mediaType);
500 
501     ResourceResponse response;
502     response.setMimeType(mimeType);
503 
504     if (base64) {
505         data = decodeURLEscapeSequences(data);
506         response.setTextEncodingName(charset);
507         client->didReceiveResponse(handle, response);
508 
509         // WebCore's decoder fails on Acid3 test 97 (whitespace).
510         Vector<char> out;
511         if (base64Decode(data.latin1().data(), data.latin1().length(), out) && out.size() > 0)
512             client->didReceiveData(handle, out.data(), out.size(), 0);
513     } else {
514         // We have to convert to UTF-16 early due to limitations in KURL
515         data = decodeURLEscapeSequences(data, TextEncoding(charset));
516         response.setTextEncodingName("UTF-16");
517         client->didReceiveResponse(handle, response);
518         if (data.length() > 0)
519             client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0);
520     }
521 
522     client->didFinishLoading(handle);
523 }
524 
dispatchSynchronousJob(ResourceHandle * job)525 void ResourceHandleManager::dispatchSynchronousJob(ResourceHandle* job)
526 {
527     KURL kurl = job->request().url();
528 
529     if (kurl.protocolIs("data")) {
530         parseDataUrl(job);
531         return;
532     }
533 
534     ResourceHandleInternal* handle = job->getInternal();
535 
536 #if LIBCURL_VERSION_NUM > 0x071200
537     // If defersLoading is true and we call curl_easy_perform
538     // on a paused handle, libcURL would do the transfert anyway
539     // and we would assert so force defersLoading to be false.
540     handle->m_defersLoading = false;
541 #endif
542 
543     initializeHandle(job);
544 
545     // curl_easy_perform blocks until the transfert is finished.
546     CURLcode ret =  curl_easy_perform(handle->m_handle);
547 
548     if (ret != 0) {
549         ResourceError error(String(handle->m_url), ret, String(handle->m_url), String(curl_easy_strerror(ret)));
550         handle->client()->didFail(job, error);
551     }
552 
553     curl_easy_cleanup(handle->m_handle);
554 }
555 
startJob(ResourceHandle * job)556 void ResourceHandleManager::startJob(ResourceHandle* job)
557 {
558     KURL kurl = job->request().url();
559 
560     if (kurl.protocolIs("data")) {
561         parseDataUrl(job);
562         return;
563     }
564 
565     initializeHandle(job);
566 
567     m_runningJobs++;
568     CURLMcode ret = curl_multi_add_handle(m_curlMultiHandle, job->getInternal()->m_handle);
569     // don't call perform, because events must be async
570     // timeout will occur and do curl_multi_perform
571     if (ret && ret != CURLM_CALL_MULTI_PERFORM) {
572 #ifndef NDEBUG
573         fprintf(stderr, "Error %d starting job %s\n", ret, encodeWithURLEscapeSequences(job->request().url().string()).latin1().data());
574 #endif
575         job->cancel();
576         return;
577     }
578 }
579 
initializeHandle(ResourceHandle * job)580 void ResourceHandleManager::initializeHandle(ResourceHandle* job)
581 {
582     KURL kurl = job->request().url();
583 
584     // Remove any fragment part, otherwise curl will send it as part of the request.
585     kurl.removeFragmentIdentifier();
586 
587     ResourceHandleInternal* d = job->getInternal();
588     String url = kurl.string();
589 
590     if (kurl.isLocalFile()) {
591         String query = kurl.query();
592         // Remove any query part sent to a local file.
593         if (!query.isEmpty()) {
594             int queryIndex = url.find(query);
595             if (queryIndex != -1)
596                 url = url.left(queryIndex - 1);
597         }
598         // Determine the MIME type based on the path.
599         d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
600     }
601 
602     d->m_handle = curl_easy_init();
603 
604 #if LIBCURL_VERSION_NUM > 0x071200
605     if (d->m_defersLoading) {
606         CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL);
607         // If we did not pause the handle, we would ASSERT in the
608         // header callback. So just assert here.
609         ASSERT(error == CURLE_OK);
610     }
611 #endif
612 #ifndef NDEBUG
613     if (getenv("DEBUG_CURL"))
614         curl_easy_setopt(d->m_handle, CURLOPT_VERBOSE, 1);
615 #endif
616     curl_easy_setopt(d->m_handle, CURLOPT_PRIVATE, job);
617     curl_easy_setopt(d->m_handle, CURLOPT_ERRORBUFFER, m_curlErrorBuffer);
618     curl_easy_setopt(d->m_handle, CURLOPT_WRITEFUNCTION, writeCallback);
619     curl_easy_setopt(d->m_handle, CURLOPT_WRITEDATA, job);
620     curl_easy_setopt(d->m_handle, CURLOPT_HEADERFUNCTION, headerCallback);
621     curl_easy_setopt(d->m_handle, CURLOPT_WRITEHEADER, job);
622     curl_easy_setopt(d->m_handle, CURLOPT_AUTOREFERER, 1);
623     curl_easy_setopt(d->m_handle, CURLOPT_FOLLOWLOCATION, 1);
624     curl_easy_setopt(d->m_handle, CURLOPT_MAXREDIRS, 10);
625     curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
626     curl_easy_setopt(d->m_handle, CURLOPT_SHARE, m_curlShareHandle);
627     curl_easy_setopt(d->m_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5); // 5 minutes
628     // FIXME: Enable SSL verification when we have a way of shipping certs
629     // and/or reporting SSL errors to the user.
630     if (ignoreSSLErrors)
631         curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYPEER, false);
632     // enable gzip and deflate through Accept-Encoding:
633     curl_easy_setopt(d->m_handle, CURLOPT_ENCODING, "");
634 
635     // url must remain valid through the request
636     ASSERT(!d->m_url);
637 
638     // url is in ASCII so latin1() will only convert it to char* without character translation.
639     d->m_url = strdup(url.latin1().data());
640     curl_easy_setopt(d->m_handle, CURLOPT_URL, d->m_url);
641 
642     if (m_cookieJarFileName) {
643         curl_easy_setopt(d->m_handle, CURLOPT_COOKIEFILE, m_cookieJarFileName);
644         curl_easy_setopt(d->m_handle, CURLOPT_COOKIEJAR, m_cookieJarFileName);
645     }
646 
647     struct curl_slist* headers = 0;
648     if (job->request().httpHeaderFields().size() > 0) {
649         HTTPHeaderMap customHeaders = job->request().httpHeaderFields();
650         HTTPHeaderMap::const_iterator end = customHeaders.end();
651         for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) {
652             String key = it->first;
653             String value = it->second;
654             String headerString(key);
655             headerString.append(": ");
656             headerString.append(value);
657             CString headerLatin1 = headerString.latin1();
658             headers = curl_slist_append(headers, headerLatin1.data());
659         }
660     }
661 
662     if ("GET" == job->request().httpMethod())
663         curl_easy_setopt(d->m_handle, CURLOPT_HTTPGET, TRUE);
664     else if ("POST" == job->request().httpMethod())
665         setupPOST(job, &headers);
666     else if ("PUT" == job->request().httpMethod())
667         setupPUT(job, &headers);
668     else if ("HEAD" == job->request().httpMethod())
669         curl_easy_setopt(d->m_handle, CURLOPT_NOBODY, TRUE);
670 
671     if (headers) {
672         curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, headers);
673         d->m_customHeaders = headers;
674     }
675 }
676 
cancel(ResourceHandle * job)677 void ResourceHandleManager::cancel(ResourceHandle* job)
678 {
679     if (removeScheduledJob(job))
680         return;
681 
682     ResourceHandleInternal* d = job->getInternal();
683     d->m_cancelled = true;
684     if (!m_downloadTimer.isActive())
685         m_downloadTimer.startOneShot(pollTimeSeconds);
686 }
687 
688 } // namespace WebCore
689