00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include "autoconfig.h"
00034 #endif
00035
00036 #ifdef HAVE_CURL
00037
00038 #include "config_manager.h"
00039 #include "tools.h"
00040 #include "curl_io_handler.h"
00041
00042 using namespace zmm;
00043
00044 CurlIOHandler::CurlIOHandler(String URL, CURL *curl_handle, size_t bufSize, size_t initialFillSize) : IOHandlerBufferHelper(bufSize, initialFillSize)
00045 {
00046 if (! string_ok(URL))
00047 throw _Exception(_("URL has not been set correctly"));
00048 if (bufSize < CURL_MAX_WRITE_SIZE)
00049 throw _Exception(_("bufSize must be at least CURL_MAX_WRITE_SIZE(")+CURL_MAX_WRITE_SIZE+')');
00050
00051 this->URL = URL;
00052 this->external_curl_handle = (curl_handle != NULL);
00053 this->curl_handle = curl_handle;
00054
00055 signalAfterEveryRead = true;
00056
00057
00058
00059 seekEnabled = true;
00060 }
00061
00062 void CurlIOHandler::open(IN enum UpnpOpenFileMode mode)
00063 {
00064 if (curl_handle == NULL)
00065 {
00066 curl_handle = curl_easy_init();
00067 if (curl_handle == NULL)
00068 throw _Exception(_("failed to init curl"));
00069 }
00070 else
00071 curl_easy_reset(curl_handle);
00072
00073 IOHandlerBufferHelper::open(mode);
00074 }
00075
00076 void CurlIOHandler::close()
00077 {
00078 IOHandlerBufferHelper::close();
00079
00080 if (external_curl_handle && curl_handle != NULL)
00081 curl_easy_cleanup(curl_handle);
00082 }
00083
00084 void CurlIOHandler::threadProc()
00085 {
00086 CURLcode res;
00087 assert(curl_handle != NULL);
00088 assert(string_ok(URL));
00089
00090
00091
00092
00093 curl_easy_setopt(curl_handle, CURLOPT_URL, URL.c_str());
00094 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
00095 curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
00096 curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, -1);
00097
00098 bool logEnabled;
00099 #ifdef TOMBDEBUG
00100 logEnabled = !ConfigManager::isDebugLogging();
00101 #else
00102 logEnabled = ConfigManager::isDebugLogging();
00103 #endif
00104 if (logEnabled)
00105 curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1);
00106
00107
00108
00109
00110
00111 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, CurlIOHandler::curlCallback);
00112 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)this);
00113
00114 AUTOLOCK_NOLOCK(mutex);
00115 do
00116 {
00117 AUTORELOCK();
00118 if (doSeek)
00119 {
00120 log_debug("SEEK: %lld %d\n", seekOffset, seekWhence);
00121
00122 if (seekWhence == SEEK_SET)
00123 {
00124 posRead = seekOffset;
00125 curl_easy_setopt(curl_handle, CURLOPT_RESUME_FROM_LARGE, seekOffset);
00126 }
00127 else if (seekWhence == SEEK_CUR)
00128 {
00129 posRead += seekOffset;
00130 curl_easy_setopt(curl_handle, CURLOPT_RESUME_FROM_LARGE, posRead);
00131 }
00132 else
00133 {
00134 log_error("CurlIOHandler currently does not support SEEK_END\n");
00135 assert(1);
00136 }
00137
00139 waitForInitialFillSize = (initialFillSize > 0);
00140
00141 doSeek = false;
00142 cond->signal();
00143 }
00144 AUTOUNLOCK();
00145 res = curl_easy_perform(curl_handle);
00146 }
00147 while (doSeek);
00148
00149 if (res != CURLE_OK)
00150 readError = true;
00151 else
00152 eof = true;
00153
00154 cond->signal();
00155 }
00156
00157
00158
00159 size_t CurlIOHandler::curlCallback(void *ptr, size_t size, size_t nmemb, void *data)
00160 {
00161 CurlIOHandler * ego = (CurlIOHandler *) data;
00162 size_t wantWrite = size * nmemb;
00163
00164 assert(wantWrite <= ego->bufSize);
00165
00166
00167
00168 AUTOLOCK(ego->mutex);
00169
00170 bool first = true;
00171
00172 int bufFree = 0;
00173 do
00174 {
00175 if (ego->doSeek && ! ego->empty &&
00176 (
00177 ego->seekWhence == SEEK_SET ||
00178 (ego->seekWhence == SEEK_CUR && ego->seekOffset > 0)
00179 )
00180 )
00181 {
00182 int currentFillSize = ego->b - ego->a;
00183 if (currentFillSize <= 0)
00184 currentFillSize += ego->bufSize;
00185
00186 int relSeek = ego->seekOffset;
00187 if (ego->seekWhence == SEEK_SET)
00188 relSeek -= ego->posRead;
00189
00190 if (relSeek <= currentFillSize)
00191 {
00192 ego->a += relSeek;
00193 ego->posRead += relSeek;
00194 if (ego->a >= ego->bufSize)
00195 ego->a -= ego->bufSize;
00196 if (ego->a == ego->b)
00197 {
00198 ego->empty = true;
00199 ego->a = ego->b = 0;
00200 }
00202
00203 ego->doSeek = false;
00204 ego->cond->signal();
00205 }
00206 }
00207
00208
00209
00210
00211 if (ego->doSeek)
00212 {
00213 ego->a = ego->b = 0;
00214 ego->empty = true;
00215
00216
00217
00218 return 0;
00219 }
00220
00221 if (! first)
00222 {
00223 ego->cond->wait();
00224 }
00225 else
00226 first = false;
00227
00228 if (ego->threadShutdown)
00229 return 0;
00230
00231 if (ego->empty)
00232 {
00233 ego->a = ego->b = 0;
00234 bufFree = ego->bufSize;
00235 }
00236 else
00237 {
00238 bufFree = ego->a - ego->b;
00239 if (bufFree < 0)
00240 bufFree += ego->bufSize;
00241 }
00242 }
00243 while ((size_t)bufFree < wantWrite);
00244
00245
00246 size_t maxWrite = (ego->empty ? ego->bufSize : (ego->a < ego->b ? ego->bufSize - ego->b : ego->a - ego->b));
00247 size_t write1 = (wantWrite > maxWrite ? maxWrite : wantWrite);
00248 size_t write2 = (write1 < wantWrite ? wantWrite - write1 : 0);
00249
00250 size_t bLocal = ego->b;
00251
00252 AUTOUNLOCK();
00253
00254 memcpy(ego->buffer + bLocal, ptr, write1);
00255 if (write2)
00256 memcpy(ego->buffer, (char *)ptr + maxWrite, write2);
00257
00258 AUTORELOCK();
00259
00260
00261 ego->b += wantWrite;
00262 if (ego->b >= ego->bufSize)
00263 ego->b -= ego->bufSize;
00264 if (ego->empty)
00265 {
00266 ego->empty = false;
00267 ego->cond->signal();
00268 }
00269 if (ego->waitForInitialFillSize)
00270 {
00271 int currentFillSize = ego->b - ego->a;
00272 if (currentFillSize <= 0)
00273 currentFillSize += ego->bufSize;
00274 if ((size_t)currentFillSize >= ego->initialFillSize)
00275 {
00276 log_debug("buffer: initial fillsize reached\n");
00277 ego->waitForInitialFillSize = false;
00278 ego->cond->signal();
00279 }
00280 }
00281
00282 return wantWrite;
00283 }
00284
00285 #endif//HAVE_CURL