void UploadQueue::start_next_upload() { unsigned int files_started = 0; boost::shared_ptr<HTTPRequestData> data(new HTTPRequestData); data->method = "POST"; data->cookies = cookies; current_upload_files.clear(); current_batch_bytes = 0; current_batch_retry = 0; for (std::list<UploadQueueEntry>::iterator it = queue.begin(); it != queue.end(); ++it) { if (it->status != UploadQueueEntry::ENTRY_WAITING) continue; try { UploadQueueEntry& qe = *it; // Open and process the image. // We do this now so we can catch errors and attribute them to individual images; // letting the HTTPRequest do it would fail the whole request if a single image // was unreadable. qe.datablock->resolve(); // Pull in post params for (std::map<std::string, std::string>::iterator pvit = post_vars.begin(); pvit != post_vars.end(); ++pvit) { qe.target.query_data[pvit->first] = pvit->second; } if (files_started == 0) { // First match sets the target uri data->uri = qe.target; } else { // Subsequently, we can only batch up more uploads to the same endpoint if (!(data->uri == qe.target)) continue; } it->setStatus(UploadQueueEntry::ENTRY_IN_PROGRESS); --files_waiting; current_queue_bytes -= qe.filesize; std::string s = "file" + files_started; qe.post_field = s; data->addFile(s, FB::wstring_to_utf8(qe.filename), "application/octet-stream", qe.datablock); current_upload_files.insert(qe.source_path); current_batch_bytes += qe.filesize; ++files_started; if (files_started >= batch_size) break; } catch (const std::exception& e) { it->result = e.what(); it->setStatus(UploadQueueEntry::ENTRY_ERROR); } } if (files_started) { // We had enough images left in the queue for another request, so kick that off now. current_upload_request = HTTPRequest::create(); current_upload_request->onStatusChanged( bind(&UploadQueue::upload_request_status_changed, this, _1) ); current_upload_request->startRequest(data); // As long as we're doing uploads, we want to keep the HTTP server // up to provide progress to the chat bar widget -- so enable deferred shutdown. // TODOTODO //try { // boost::shared_ptr<HTTPService> h = http_srv_inst_weak.lock(); // if (h) h->setDeferShutdown(true); //} catch (...) {} } else { #ifndef NDEBUG FBLOG_DEBUG("UploadQueue", "start_next_upload() found no waiting files, running completion handlers\n"); #endif // All done, post upload finished callback to all instances FB::VariantMap d = getEmptyProgressDict(); FB::VariantMap failures; for (std::list<UploadQueueEntry>::iterator it = queue.begin(); it != queue.end(); ++it) { if (it->status == UploadQueueEntry::ENTRY_ERROR) { failures[FB::wstring_to_utf8(it->source_path)] = it->result; #ifndef NDEBUG FBLOG_WARN("UploadQueue", "Reporting file \"" << it->source_path.c_str() << "\" as failed: " << it->result.c_str() << std::endl); #endif } } queue.clear(); if (! failures.empty()) d["failed_files"] = failures; status = UploadQueue::UPLOAD_COMPLETE; // fire completion handlers, if available for (std::list<FB::URI>::iterator it = completion_handlers.begin(); it != completion_handlers.end(); ++it) { boost::shared_ptr<HTTPRequestData> reqdata(new HTTPRequestData(*it)); reqdata->cookies = cookies; HTTPRequest::asyncStartRequest(reqdata); } if (queue_finished_callback) queue_finished_callback(shared_ptr()); StatusUpdateEvent evt(d); SendEvent(&evt); } }