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