2006-10-14 Krzysztof Kowalczyk <kkowalczyk@gmail.com>
[WebKit-https.git] / WebCore / platform / gdk / ResourceLoaderManager.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com 
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26  */
27
28 #include "config.h"
29 #include "ResourceLoaderManager.h"
30
31 #include "ResourceLoader.h"
32 #include "ResourceLoaderInternal.h"
33
34 namespace WebCore {
35
36 const int selectTimeoutMS = 5;
37 const double pollTimeSeconds = 0.05;
38
39 ResourceLoaderManager::ResourceLoaderManager()
40     : m_useSimple(false)
41     , jobs(new HashSet<ResourceLoader*>)
42     , m_downloadTimer(this, &ResourceLoaderManager::downloadTimerCallback)
43 {
44     curl_global_init(CURL_GLOBAL_ALL);
45     curlMultiHandle = curl_multi_init();
46 }
47
48 ResourceLoaderManager* ResourceLoaderManager::get()
49 {
50     static ResourceLoaderManager* singleton;
51     if (!singleton)
52         singleton = new ResourceLoaderManager;
53     return singleton;
54 }
55
56 void ResourceLoaderManager::useSimpleTransfer(bool useSimple)
57 {
58     m_useSimple = useSimple;
59 }
60
61 static size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* obj)
62 {
63     ResourceLoader* job = static_cast<ResourceLoader*>(obj);
64     ResourceLoaderInternal* d = job->getInternal();
65     int totalSize = size * nmemb;
66     d->client->receivedData(job, static_cast<char*>(ptr), totalSize);
67     return totalSize;
68 }
69
70 static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
71 {
72     int totalSize = size * nmemb;
73     return totalSize;
74 }
75
76 void ResourceLoaderManager::downloadTimerCallback(Timer<ResourceLoaderManager>* timer)
77 {
78     if (jobs->isEmpty()) {
79         m_downloadTimer.stop();
80         return;
81     }
82     if (m_useSimple) {
83         for (HashSet<ResourceLoader*>::iterator it = jobs->begin(); it != jobs->end(); ++it) {
84             ResourceLoader* job = *it;
85             ResourceLoaderInternal* d = job->getInternal();
86             CURLcode res = curl_easy_perform(d->m_handle);
87             if (res != CURLE_OK)
88                 printf("Error WITH JOB %d\n", res);
89             d->client->receivedAllData(job, 0);
90             d->client->receivedAllData(job);
91             curl_easy_cleanup(d->m_handle);
92             d->m_handle = 0;
93         }
94         jobs->clear();
95         m_downloadTimer.stop();
96     } else {
97         FD_ZERO(&fdread);
98         FD_ZERO(&fdwrite);
99         FD_ZERO(&fdexcep);
100         curl_multi_fdset(curlMultiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
101         int nrunning;
102         struct timeval timeout;
103         int retval;
104         timeout.tv_sec = 0;
105         timeout.tv_usec = selectTimeoutMS * 1000;       // select waits microseconds
106         retval = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
107         switch (retval) {
108             case -1:                        // select error
109 #ifndef NDEBUG
110                 printf("%s, select error(%d)\n", __PRETTY_FUNCTION__,retval);
111 #endif
112                 /* fallthrough*/
113             case 0:                 // select timeout
114 #ifndef NDEBUG
115                 printf("%s, select timeout %d\n", __PRETTY_FUNCTION__,retval);
116 #endif
117                 /* fallthrough. this can be the first perform to be made */
118             default:                        // 1+ descriptors have data
119                 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curlMultiHandle, &nrunning))
120                     { }
121         }
122
123         // check the curl messages indicating completed transfers
124         // and free their resources
125         int nmsgs;
126         while (CURLMsg* msg = curl_multi_info_read(curlMultiHandle, &nmsgs)) {
127             if (msg->msg == CURLMSG_DONE) {
128                 // find the node which has same d->m_handle as completed transfer
129                 CURL* chandle = msg->easy_handle;
130                 assert(chandle);
131                 ResourceLoader *job;
132                 curl_easy_getinfo(chandle, CURLINFO_PRIVATE, &job);
133                 assert(job); //fixme: assert->if ?
134                 // if found, delete it
135                 if (job) {
136                     ResourceLoaderInternal *d = job->getInternal();
137                     switch (msg->data.result) {
138                         case CURLE_OK: {
139                             // use this to authenticate
140                             int respCode = -1;
141                             curl_easy_getinfo(d->m_handle, CURLINFO_RESPONSE_CODE, &respCode);
142                             remove(job);
143                             break;
144                         }
145                         default:
146                             printf("Curl ERROR %s\n", curl_easy_strerror(msg->data.result));
147                             job->setError(msg->data.result);
148                             remove(job);
149                             break;
150                     }
151                 } else {
152                     printf("CurlRequest not found, eventhough curl d->m_handle finished\n");
153                     assert(0);
154                 }
155             }
156
157         }
158     }
159     if (!jobs->isEmpty())
160         m_downloadTimer.startOneShot(pollTimeSeconds);
161 }
162
163 void ResourceLoaderManager::remove(ResourceLoader* job)
164 {
165     ResourceLoaderInternal* d = job->getInternal();
166     if (!d->m_handle)
167         return;
168     if (jobs->contains(job))
169         jobs->remove(job);
170     if (jobs->isEmpty())
171         m_downloadTimer.stop();
172     d->client->receivedAllData(job, 0);
173     d->client->receivedAllData(job);
174     if (d->m_handle) {
175         curl_multi_remove_handle(curlMultiHandle, d->m_handle);
176         curl_easy_cleanup(d->m_handle);
177         d->m_handle = NULL;
178     }
179 }
180
181 void ResourceLoaderManager::add(ResourceLoader* job)
182 {
183     bool startTimer = jobs->isEmpty();
184     ResourceLoaderInternal* d = job->getInternal();
185     DeprecatedString url = d->URL.url();
186     d->m_handle = curl_easy_init();
187     curl_easy_setopt(d->m_handle, CURLOPT_PRIVATE, job);
188     curl_easy_setopt(d->m_handle, CURLOPT_ERRORBUFFER, error_buffer);
189     curl_easy_setopt(d->m_handle, CURLOPT_WRITEFUNCTION, writeCallback);
190     curl_easy_setopt(d->m_handle, CURLOPT_WRITEDATA, job);
191     curl_easy_setopt(d->m_handle, CURLOPT_HEADERFUNCTION, headerCallback);
192     curl_easy_setopt(d->m_handle, CURLOPT_WRITEHEADER, job);
193     curl_easy_setopt(d->m_handle, CURLOPT_FOLLOWLOCATION, 1);
194     curl_easy_setopt(d->m_handle, CURLOPT_MAXREDIRS, 10);
195     curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
196     // url ptr must remain valid through the request
197     curl_easy_setopt(d->m_handle, CURLOPT_URL, url.ascii());
198
199     if (m_useSimple)
200         jobs->add(job);
201     else {
202         CURLMcode ret = curl_multi_add_handle(curlMultiHandle, d->m_handle);
203         // don't call perform, because events must be async
204         // timeout will occur and do curl_multi_perform
205         if (ret && ret != CURLM_CALL_MULTI_PERFORM) {
206             printf("Error %d starting job %s\n", ret, d->URL.url().ascii());
207             job->setError(1);
208             startTimer =false;
209         } else
210             jobs->add(job);
211     }
212     if (startTimer)
213         m_downloadTimer.startOneShot(pollTimeSeconds);
214 }
215
216 void ResourceLoaderManager::cancel(ResourceLoader* job)
217 {
218     remove(job);
219     job->setError(1);
220 }
221
222 } // namespace WebCore