2005-01-20 23:22:22 +00:00
|
|
|
// Berkeley Open Infrastructure for Network Computing
|
|
|
|
// http://boinc.berkeley.edu
|
|
|
|
// Copyright (C) 2005 University of California
|
2003-07-02 02:05:44 +00:00
|
|
|
//
|
2005-01-20 23:22:22 +00:00
|
|
|
// This is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation;
|
|
|
|
// either version 2.1 of the License, or (at your option) any later version.
|
2003-07-02 02:05:44 +00:00
|
|
|
//
|
2005-01-20 23:22:22 +00:00
|
|
|
// This software is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
// See the GNU Lesser General Public License for more details.
|
2002-06-10 06:14:18 +00:00
|
|
|
//
|
2005-01-20 23:22:22 +00:00
|
|
|
// To view the GNU Lesser General Public License visit
|
|
|
|
// http://www.gnu.org/copyleft/lesser.html
|
|
|
|
// or write to the Free Software Foundation, Inc.,
|
|
|
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2002-06-10 06:14:18 +00:00
|
|
|
|
2003-10-16 19:03:49 +00:00
|
|
|
#include "cpp.h"
|
2002-08-09 21:43:19 +00:00
|
|
|
|
2004-03-04 11:41:43 +00:00
|
|
|
#ifdef _WIN32
|
2004-06-16 23:16:08 +00:00
|
|
|
#include "boinc_win.h"
|
2004-03-04 11:41:43 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
2005-11-21 18:34:44 +00:00
|
|
|
#include "config.h"
|
2004-07-13 13:54:09 +00:00
|
|
|
#include <cmath>
|
|
|
|
#include <cstdlib>
|
2004-03-04 11:41:43 +00:00
|
|
|
#endif
|
2002-08-07 22:52:10 +00:00
|
|
|
|
|
|
|
#include "error_numbers.h"
|
2003-06-16 21:23:58 +00:00
|
|
|
#include "md5_file.h"
|
2002-08-07 22:52:10 +00:00
|
|
|
#include "parse.h"
|
|
|
|
#include "util.h"
|
2003-08-01 21:20:20 +00:00
|
|
|
#include "filesys.h"
|
2002-08-07 22:52:10 +00:00
|
|
|
|
2004-04-08 08:15:23 +00:00
|
|
|
#include "log_flags.h"
|
|
|
|
#include "file_names.h"
|
|
|
|
#include "client_state.h"
|
|
|
|
#include "client_types.h"
|
|
|
|
#include "client_msgs.h"
|
|
|
|
|
2004-06-30 18:17:21 +00:00
|
|
|
using std::vector;
|
|
|
|
|
2003-02-04 21:47:12 +00:00
|
|
|
PERS_FILE_XFER::PERS_FILE_XFER() {
|
2002-08-07 22:52:10 +00:00
|
|
|
nretry = 0;
|
2005-06-07 19:22:50 +00:00
|
|
|
first_request_time = gstate.now;
|
2002-08-07 22:52:10 +00:00
|
|
|
next_request_time = first_request_time;
|
2003-02-06 03:39:10 +00:00
|
|
|
time_so_far = 0;
|
2006-02-18 01:57:24 +00:00
|
|
|
last_bytes_xferred = 0;
|
2007-01-19 20:56:49 +00:00
|
|
|
pers_xfer_done = false;
|
2003-07-23 22:24:28 +00:00
|
|
|
fip = NULL;
|
|
|
|
fxp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PERS_FILE_XFER::~PERS_FILE_XFER() {
|
|
|
|
if (fip) {
|
|
|
|
fip->pers_file_xfer = NULL;
|
|
|
|
}
|
2003-02-04 21:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int PERS_FILE_XFER::init(FILE_INFO* f, bool is_file_upload) {
|
|
|
|
fxp = NULL;
|
|
|
|
fip = f;
|
2002-08-07 22:52:10 +00:00
|
|
|
is_upload = is_file_upload;
|
2004-09-04 05:21:33 +00:00
|
|
|
pers_xfer_done = false;
|
2005-03-09 23:47:45 +00:00
|
|
|
const char* p = f->get_init_url(is_file_upload);
|
2004-07-06 18:31:56 +00:00
|
|
|
if (!p) {
|
2007-01-25 23:39:06 +00:00
|
|
|
msg_printf(NULL, MSG_INTERNAL_ERROR, "No URL for file transfer of %s", f->name);
|
2004-07-06 18:31:56 +00:00
|
|
|
return ERR_NULL;
|
|
|
|
}
|
2002-08-07 22:52:10 +00:00
|
|
|
return 0;
|
2002-06-10 06:14:18 +00:00
|
|
|
}
|
|
|
|
|
2002-08-07 22:52:10 +00:00
|
|
|
// Try to start the file transfer associated with this persistent file transfer.
|
|
|
|
//
|
2003-05-16 19:22:57 +00:00
|
|
|
int PERS_FILE_XFER::start_xfer() {
|
2002-08-07 22:52:10 +00:00
|
|
|
FILE_XFER *file_xfer;
|
|
|
|
int retval;
|
2003-02-26 00:47:57 +00:00
|
|
|
|
2002-08-07 22:52:10 +00:00
|
|
|
// Decide whether to start a new file transfer
|
2002-08-14 20:31:27 +00:00
|
|
|
//
|
2003-12-02 22:47:32 +00:00
|
|
|
if (!gstate.start_new_file_xfer(*this)) {
|
2003-05-16 19:22:57 +00:00
|
|
|
return ERR_IDLE_PERIOD;
|
2002-08-21 19:12:42 +00:00
|
|
|
}
|
2003-06-09 23:50:49 +00:00
|
|
|
|
2003-08-01 21:20:20 +00:00
|
|
|
// Does the file exist already? this could happen for example if we are
|
2003-12-02 22:47:32 +00:00
|
|
|
// downloading an application which exists from a previous installation
|
|
|
|
//
|
2003-08-01 21:20:20 +00:00
|
|
|
if (!is_upload) {
|
|
|
|
char pathname[256];
|
|
|
|
get_pathname(fip, pathname);
|
2005-04-06 19:41:31 +00:00
|
|
|
|
2005-04-12 22:53:35 +00:00
|
|
|
// see if file already exists and is valid
|
2005-04-06 19:41:31 +00:00
|
|
|
//
|
2007-01-19 20:56:49 +00:00
|
|
|
if (!fip->verify_file(true, false)) {
|
2005-04-12 21:54:40 +00:00
|
|
|
retval = fip->set_permissions();
|
|
|
|
fip->status = FILE_PRESENT;
|
|
|
|
pers_xfer_done = true;
|
2003-08-01 21:20:20 +00:00
|
|
|
|
2006-12-06 20:59:29 +00:00
|
|
|
if (log_flags.file_xfer) {
|
|
|
|
msg_printf(
|
|
|
|
fip->project, MSG_INFO,
|
|
|
|
"File %s exists already, skipping download", fip->name
|
|
|
|
);
|
|
|
|
}
|
2004-07-13 13:54:09 +00:00
|
|
|
|
2005-04-12 21:54:40 +00:00
|
|
|
return 0;
|
2005-08-16 17:43:32 +00:00
|
|
|
} else {
|
|
|
|
fip->status = FILE_NOT_PRESENT;
|
2003-08-01 21:20:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-08-21 19:12:42 +00:00
|
|
|
// Create a new FILE_XFER object and initialize a
|
|
|
|
// download or upload for the persistent file transfer
|
|
|
|
//
|
|
|
|
file_xfer = new FILE_XFER;
|
2004-09-26 04:16:52 +00:00
|
|
|
file_xfer->set_proxy(&gstate.proxy_info);
|
2002-08-21 19:12:42 +00:00
|
|
|
if (is_upload) {
|
2003-03-31 23:18:55 +00:00
|
|
|
if (gstate.exit_before_upload) {
|
|
|
|
exit(0);
|
|
|
|
}
|
2002-08-21 19:12:42 +00:00
|
|
|
retval = file_xfer->init_upload(*fip);
|
|
|
|
} else {
|
|
|
|
retval = file_xfer->init_download(*fip);
|
|
|
|
}
|
2004-06-03 21:19:18 +00:00
|
|
|
fxp = file_xfer;
|
2004-10-25 20:16:30 +00:00
|
|
|
if (!retval) retval = gstate.file_xfers->insert(file_xfer);
|
2002-08-21 19:12:42 +00:00
|
|
|
if (retval) {
|
2007-01-25 23:39:06 +00:00
|
|
|
if (log_flags.http_debug) {
|
|
|
|
msg_printf(
|
|
|
|
fip->project, MSG_INFO, "[file_xfer_debug] Couldn't start %s of %s",
|
|
|
|
(is_upload ? "upload" : "download"), fip->name
|
|
|
|
);
|
|
|
|
msg_printf(
|
|
|
|
fip->project, MSG_INFO, "[file_xfer_debug] URL %s: %s",
|
|
|
|
fip->get_current_url(is_upload), boincerror(retval)
|
|
|
|
);
|
|
|
|
}
|
2004-10-25 20:16:30 +00:00
|
|
|
|
2003-05-16 19:22:57 +00:00
|
|
|
fxp->file_xfer_retval = retval;
|
2003-05-20 00:03:39 +00:00
|
|
|
handle_xfer_failure();
|
|
|
|
delete fxp;
|
2003-05-16 19:22:57 +00:00
|
|
|
fxp = NULL;
|
|
|
|
return retval;
|
2002-08-07 22:52:10 +00:00
|
|
|
}
|
2003-05-16 19:22:57 +00:00
|
|
|
if (log_flags.file_xfer) {
|
2003-07-03 05:01:29 +00:00
|
|
|
msg_printf(
|
2006-12-06 20:59:29 +00:00
|
|
|
fip->project, MSG_INFO, "[file_xfer] Started %s of file %s",
|
2003-07-03 05:01:29 +00:00
|
|
|
(is_upload ? "upload" : "download"), fip->name
|
|
|
|
);
|
2003-05-16 19:22:57 +00:00
|
|
|
}
|
2006-06-22 19:40:30 +00:00
|
|
|
if (log_flags.file_xfer_debug) {
|
|
|
|
msg_printf(0, MSG_INFO,
|
2006-09-07 20:39:25 +00:00
|
|
|
"[file_xfer_debug] PERS_FILE_XFER::start_xfer(): URL: %s\n",
|
2006-06-22 19:40:30 +00:00
|
|
|
fip->get_current_url(is_upload)
|
|
|
|
);
|
|
|
|
}
|
2003-05-16 19:22:57 +00:00
|
|
|
return 0;
|
2002-06-10 06:14:18 +00:00
|
|
|
}
|
|
|
|
|
2002-08-14 20:31:27 +00:00
|
|
|
// Poll the status of this persistent file transfer.
|
|
|
|
// If it's time to start it, then attempt to start it.
|
|
|
|
// If it has finished or failed, then deal with it appropriately
|
2002-08-07 22:52:10 +00:00
|
|
|
//
|
2005-06-07 19:22:50 +00:00
|
|
|
bool PERS_FILE_XFER::poll() {
|
2002-08-07 22:52:10 +00:00
|
|
|
int retval;
|
2003-06-09 23:50:49 +00:00
|
|
|
|
2004-09-04 05:21:33 +00:00
|
|
|
if (pers_xfer_done) {
|
2002-08-22 00:16:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!fxp) {
|
2002-08-14 20:31:27 +00:00
|
|
|
// No file xfer is active.
|
2003-12-02 22:47:32 +00:00
|
|
|
// Either initial or resume after failure.
|
2002-08-14 20:31:27 +00:00
|
|
|
// See if it's time to try again.
|
|
|
|
//
|
2005-09-30 21:29:31 +00:00
|
|
|
if (gstate.now < next_request_time) {
|
|
|
|
return false;
|
|
|
|
}
|
2005-10-14 04:41:53 +00:00
|
|
|
#if 0
|
2005-09-30 21:29:31 +00:00
|
|
|
if (gstate.now < fip->project->next_file_xfer_time(is_upload)) {
|
2002-08-20 00:30:13 +00:00
|
|
|
return false;
|
|
|
|
}
|
2005-10-14 04:41:53 +00:00
|
|
|
#endif
|
2005-09-30 21:29:31 +00:00
|
|
|
last_time = gstate.now;
|
|
|
|
fip->upload_offset = -1;
|
|
|
|
retval = start_xfer();
|
|
|
|
return (retval == 0);
|
2002-08-14 20:31:27 +00:00
|
|
|
}
|
2002-08-07 22:52:10 +00:00
|
|
|
|
2006-02-18 01:57:24 +00:00
|
|
|
// copy bytes_xferred for use in GUI
|
|
|
|
last_bytes_xferred = fxp->bytes_xferred;
|
2006-02-23 20:37:36 +00:00
|
|
|
if (fxp->is_upload) {
|
|
|
|
last_bytes_xferred += fxp->file_offset;
|
|
|
|
}
|
2006-02-18 01:57:24 +00:00
|
|
|
|
2004-09-02 23:06:14 +00:00
|
|
|
// don't count suspended periods in total time
|
|
|
|
//
|
2005-06-07 19:22:50 +00:00
|
|
|
double diff = gstate.now - last_time;
|
2004-09-02 23:06:14 +00:00
|
|
|
if (diff <= 2) {
|
|
|
|
time_so_far += diff;
|
2003-02-04 21:47:12 +00:00
|
|
|
}
|
2005-06-07 19:22:50 +00:00
|
|
|
last_time = gstate.now;
|
2003-02-03 22:47:08 +00:00
|
|
|
|
2002-08-14 20:31:27 +00:00
|
|
|
if (fxp->file_xfer_done) {
|
2006-06-22 19:40:30 +00:00
|
|
|
if (log_flags.file_xfer_debug) {
|
|
|
|
msg_printf(0, MSG_INFO,
|
2006-09-07 20:39:25 +00:00
|
|
|
"[file_xfer_debug] PERS_FILE_XFER::poll(): file transfer status %d",
|
2006-06-22 19:40:30 +00:00
|
|
|
fxp->file_xfer_retval
|
|
|
|
);
|
|
|
|
}
|
2004-09-02 23:06:14 +00:00
|
|
|
if (fxp->file_xfer_retval == 0) {
|
|
|
|
// The transfer finished with no errors.
|
2005-09-30 21:29:31 +00:00
|
|
|
fip->project->file_xfer_succeeded(is_upload);
|
2004-09-02 23:06:14 +00:00
|
|
|
if (log_flags.file_xfer) {
|
|
|
|
msg_printf(
|
2006-12-06 20:59:29 +00:00
|
|
|
fip->project, MSG_INFO, "[file_xfer] Finished %s of file %s",
|
2004-09-02 23:06:14 +00:00
|
|
|
is_upload?"upload":"download", fip->name
|
2005-01-13 18:57:27 +00:00
|
|
|
);
|
2004-09-02 23:06:14 +00:00
|
|
|
if (fxp->xfer_speed < 0) {
|
2006-12-06 20:59:29 +00:00
|
|
|
msg_printf(fip->project, MSG_INFO, "[file_xfer] No data transferred");
|
2004-09-02 23:06:14 +00:00
|
|
|
} else {
|
2004-07-14 21:28:59 +00:00
|
|
|
msg_printf(
|
2006-12-06 20:59:29 +00:00
|
|
|
fip->project, MSG_INFO, "[file_xfer] Throughput %d bytes/sec",
|
2004-09-02 23:06:14 +00:00
|
|
|
(int)fxp->xfer_speed
|
2004-08-19 15:36:30 +00:00
|
|
|
);
|
2004-07-14 21:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
2004-09-04 05:21:33 +00:00
|
|
|
pers_xfer_done = true;
|
2004-09-02 23:06:14 +00:00
|
|
|
} else if (fxp->file_xfer_retval == ERR_UPLOAD_PERMANENT) {
|
|
|
|
if (log_flags.file_xfer) {
|
|
|
|
msg_printf(
|
2006-12-06 20:59:29 +00:00
|
|
|
fip->project, MSG_INFO, "[file_xfer] Permanently failed %s of %s",
|
2004-09-02 23:06:14 +00:00
|
|
|
is_upload?"upload":"download", fip->name
|
|
|
|
);
|
|
|
|
}
|
2006-06-19 04:29:26 +00:00
|
|
|
try_next_url("server rejected file");
|
|
|
|
} else if (fxp->file_xfer_retval == ERR_NOT_FOUND) {
|
|
|
|
if (log_flags.file_xfer) {
|
|
|
|
msg_printf(
|
2006-12-06 20:59:29 +00:00
|
|
|
fip->project, MSG_INFO, "[file_xfer] Permanently failed %s of %s",
|
2006-06-19 04:29:26 +00:00
|
|
|
is_upload?"upload":"download", fip->name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
try_next_url("File not found on client");
|
2004-09-02 23:06:14 +00:00
|
|
|
} else {
|
|
|
|
if (log_flags.file_xfer) {
|
|
|
|
msg_printf(
|
2006-12-06 20:59:29 +00:00
|
|
|
fip->project, MSG_INFO, "[file_xfer] Temporarily failed %s of %s: %s",
|
2005-03-09 23:47:45 +00:00
|
|
|
is_upload?"upload":"download", fip->name,
|
2005-07-05 09:02:55 +00:00
|
|
|
boincerror(fxp->file_xfer_retval)
|
2004-09-02 23:06:14 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
handle_xfer_failure();
|
2004-08-19 15:36:30 +00:00
|
|
|
}
|
2004-09-02 23:06:14 +00:00
|
|
|
|
2006-06-19 04:29:26 +00:00
|
|
|
// fxp could have already been freed and zeroed by try_next_url
|
2005-03-01 19:56:30 +00:00
|
|
|
// so check before trying to remove
|
|
|
|
//
|
|
|
|
if (fxp) {
|
|
|
|
gstate.file_xfers->remove(fxp);
|
|
|
|
delete fxp;
|
|
|
|
fxp = NULL;
|
|
|
|
}
|
2004-09-02 23:06:14 +00:00
|
|
|
return true;
|
2002-06-10 06:14:18 +00:00
|
|
|
}
|
2002-08-07 22:52:10 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-06-19 04:29:26 +00:00
|
|
|
void PERS_FILE_XFER::xfer_failed(const char* why) {
|
|
|
|
gstate.file_xfers->remove(fxp);
|
|
|
|
delete fxp;
|
|
|
|
fxp = NULL;
|
|
|
|
if (is_upload) {
|
|
|
|
fip->status = ERR_GIVEUP_UPLOAD;
|
|
|
|
} else {
|
|
|
|
fip->status = ERR_GIVEUP_DOWNLOAD;
|
|
|
|
}
|
|
|
|
pers_xfer_done = true;
|
2007-01-25 23:39:06 +00:00
|
|
|
if (log_flags.file_xfer) {
|
|
|
|
msg_printf(
|
|
|
|
fip->project, MSG_INFO, "Giving up on %s of %s: %s",
|
|
|
|
is_upload?"upload":"download", fip->name, why
|
|
|
|
);
|
|
|
|
}
|
2006-06-19 04:29:26 +00:00
|
|
|
fip->error_msg = why;
|
|
|
|
}
|
|
|
|
|
2004-09-04 05:21:33 +00:00
|
|
|
// A file transfer (to a particular server)
|
2004-09-26 04:16:52 +00:00
|
|
|
// has had a failure
|
|
|
|
// TODO ?? transient ? permanent? terminology??
|
2004-09-04 05:21:33 +00:00
|
|
|
//
|
2004-07-27 23:29:45 +00:00
|
|
|
// Takes a reason why a transfer has failed.
|
|
|
|
//
|
|
|
|
// Checks to see if there are no more valid URLs listed in the file_info.
|
|
|
|
//
|
|
|
|
// If no more urls are present, the file is then given up on, the reason is
|
|
|
|
// listed in the error_msg field, and the appropriate status code is given.
|
|
|
|
//
|
|
|
|
// If there are more URLs to try, the file_xfer is restarted with these new
|
|
|
|
// urls until a good transfer is made or it completely gives up.
|
2004-07-06 17:37:58 +00:00
|
|
|
//
|
2006-06-19 04:29:26 +00:00
|
|
|
void PERS_FILE_XFER::try_next_url(const char* why) {
|
2004-08-12 10:13:01 +00:00
|
|
|
if (fip->get_next_url(fip->upload_when_present) == NULL) {
|
2006-06-19 04:29:26 +00:00
|
|
|
xfer_failed(why);
|
2004-07-27 23:29:45 +00:00
|
|
|
fip->delete_file();
|
|
|
|
} else {
|
|
|
|
if (is_upload) {
|
|
|
|
if (gstate.exit_before_upload) {
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
fxp->init_upload(*fip);
|
|
|
|
} else {
|
|
|
|
fxp->init_download(*fip);
|
|
|
|
}
|
|
|
|
}
|
2003-05-06 21:43:26 +00:00
|
|
|
}
|
|
|
|
|
2002-08-21 19:12:42 +00:00
|
|
|
// Handle a transfer failure
|
|
|
|
//
|
2003-05-20 00:03:39 +00:00
|
|
|
void PERS_FILE_XFER::handle_xfer_failure() {
|
2003-03-13 21:49:52 +00:00
|
|
|
|
2002-08-21 19:12:42 +00:00
|
|
|
// If it was a bad range request, delete the file and start over
|
2003-03-13 21:49:52 +00:00
|
|
|
//
|
2002-08-26 22:57:17 +00:00
|
|
|
if (fxp->file_xfer_retval == HTTP_STATUS_RANGE_REQUEST_ERROR) {
|
2002-08-21 19:12:42 +00:00
|
|
|
fip->delete_file();
|
2004-07-27 23:29:45 +00:00
|
|
|
return;
|
2002-08-21 19:12:42 +00:00
|
|
|
}
|
2003-02-04 21:47:12 +00:00
|
|
|
|
2003-09-30 18:09:58 +00:00
|
|
|
if (fxp->file_xfer_retval == HTTP_STATUS_NOT_FOUND) {
|
2005-10-07 19:19:07 +00:00
|
|
|
if (fxp->is_upload) {
|
|
|
|
// If it is uploading and receives a HTTP_STATUS_NOT_FOUND then
|
|
|
|
// the file upload handler could not be found.
|
|
|
|
// This is hopefully transient.
|
|
|
|
//
|
|
|
|
retry_or_backoff();
|
2004-06-17 02:36:08 +00:00
|
|
|
return;
|
|
|
|
} else {
|
2006-06-19 04:29:26 +00:00
|
|
|
try_next_url("file was not found on server");
|
2004-07-27 23:29:45 +00:00
|
|
|
return;
|
2004-06-17 02:36:08 +00:00
|
|
|
}
|
2003-09-30 18:09:58 +00:00
|
|
|
}
|
2003-06-09 23:50:49 +00:00
|
|
|
|
2002-08-21 19:12:42 +00:00
|
|
|
// See if it's time to give up on the persistent file xfer
|
|
|
|
//
|
2005-06-07 19:22:50 +00:00
|
|
|
if ((gstate.now - first_request_time) > gstate.file_xfer_giveup_period) {
|
2006-06-19 04:29:26 +00:00
|
|
|
try_next_url("too much elapsed time");
|
2003-09-30 18:09:58 +00:00
|
|
|
} else {
|
|
|
|
retry_or_backoff();
|
2002-08-30 20:56:02 +00:00
|
|
|
}
|
2002-08-21 19:12:42 +00:00
|
|
|
}
|
2006-01-19 01:13:49 +00:00
|
|
|
|
2002-08-21 19:12:42 +00:00
|
|
|
// Cycle to the next URL, or if we've hit all URLs in this cycle,
|
|
|
|
// backoff and try again later
|
|
|
|
//
|
2003-05-20 00:03:39 +00:00
|
|
|
void PERS_FILE_XFER::retry_or_backoff() {
|
2002-08-21 19:12:42 +00:00
|
|
|
// Cycle to the next URL to try
|
|
|
|
// If we reach the URL that we started at, then we have tried all
|
|
|
|
// servers without success
|
2003-02-26 00:47:57 +00:00
|
|
|
//
|
2004-07-06 18:31:56 +00:00
|
|
|
|
|
|
|
if (fip->get_next_url(is_upload) == NULL) {
|
2005-09-30 21:29:31 +00:00
|
|
|
fip->project->file_xfer_failed(is_upload);
|
|
|
|
do_backoff();
|
2002-08-21 19:12:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-30 21:29:31 +00:00
|
|
|
// per-file backoff policy: sets next_request_time
|
|
|
|
//
|
|
|
|
void PERS_FILE_XFER::do_backoff() {
|
|
|
|
double backoff = 0;
|
|
|
|
|
2006-04-18 23:30:32 +00:00
|
|
|
// don't count it as a server failure if network is down
|
|
|
|
//
|
2006-07-17 22:18:17 +00:00
|
|
|
if (!net_status.need_physical_connection) {
|
2006-04-18 23:30:32 +00:00
|
|
|
nretry++;
|
|
|
|
}
|
2005-09-30 21:29:31 +00:00
|
|
|
|
|
|
|
// Do an exponential backoff of e^nretry seconds,
|
|
|
|
// keeping within the bounds of pers_retry_delay_min and
|
|
|
|
// pers_retry_delay_max
|
|
|
|
//
|
|
|
|
backoff = calculate_exponential_backoff(
|
|
|
|
nretry, gstate.pers_retry_delay_min, gstate.pers_retry_delay_max
|
|
|
|
);
|
|
|
|
next_request_time = gstate.now + backoff;
|
|
|
|
msg_printf(fip->project, MSG_INFO,
|
|
|
|
"Backing off %s on %s of file %s",
|
|
|
|
timediff_format(backoff).c_str(),
|
|
|
|
is_upload?"upload":"download",
|
|
|
|
fip->name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2004-09-26 04:16:52 +00:00
|
|
|
void PERS_FILE_XFER::abort() {
|
|
|
|
if (fxp) {
|
|
|
|
gstate.file_xfers->remove(fxp);
|
|
|
|
delete fxp;
|
|
|
|
fxp = NULL;
|
|
|
|
}
|
|
|
|
fip->status = is_upload?ERR_GIVEUP_UPLOAD:ERR_GIVEUP_DOWNLOAD;
|
|
|
|
fip->error_msg = "user requested transfer abort";
|
|
|
|
pers_xfer_done = true;
|
|
|
|
}
|
|
|
|
|
2005-03-01 19:56:30 +00:00
|
|
|
// Parse XML information about a persistent file transfer
|
2002-08-07 22:52:10 +00:00
|
|
|
//
|
2004-06-12 04:45:36 +00:00
|
|
|
int PERS_FILE_XFER::parse(MIOFILE& fin) {
|
2003-06-03 22:47:15 +00:00
|
|
|
char buf[256];
|
2002-08-15 22:03:41 +00:00
|
|
|
|
2004-06-12 04:45:36 +00:00
|
|
|
while (fin.fgets(buf, 256)) {
|
2002-08-07 22:52:10 +00:00
|
|
|
if (match_tag(buf, "</persistent_file_xfer>")) return 0;
|
|
|
|
else if (parse_int(buf, "<num_retries>", nretry)) continue;
|
2004-12-01 20:56:20 +00:00
|
|
|
else if (parse_double(buf, "<first_request_time>", first_request_time)) {
|
|
|
|
validate_time(first_request_time);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (parse_double(buf, "<next_request_time>", next_request_time)) {
|
|
|
|
validate_time(next_request_time);
|
|
|
|
continue;
|
|
|
|
}
|
2003-02-03 22:47:08 +00:00
|
|
|
else if (parse_double(buf, "<time_so_far>", time_so_far)) continue;
|
2006-02-18 01:57:24 +00:00
|
|
|
else if (parse_double(buf, "<last_bytes_xferred>", last_bytes_xferred)) continue;
|
2003-03-13 21:49:52 +00:00
|
|
|
else {
|
2006-12-06 20:59:29 +00:00
|
|
|
if (log_flags.unparsed_xml) {
|
2007-01-25 23:39:06 +00:00
|
|
|
msg_printf(NULL, MSG_INFO,
|
|
|
|
"[unparsed_xml] Unparsed line in file transfer info: %s", buf
|
2006-12-06 20:59:29 +00:00
|
|
|
);
|
|
|
|
}
|
2003-03-13 21:49:52 +00:00
|
|
|
}
|
2002-08-07 22:52:10 +00:00
|
|
|
}
|
2003-10-21 18:18:41 +00:00
|
|
|
return ERR_XML_PARSE;
|
2002-08-07 22:52:10 +00:00
|
|
|
}
|
|
|
|
|
2005-03-01 19:56:30 +00:00
|
|
|
// Write XML information about a persistent file transfer
|
2002-08-07 22:52:10 +00:00
|
|
|
//
|
2004-06-12 04:45:36 +00:00
|
|
|
int PERS_FILE_XFER::write(MIOFILE& fout) {
|
|
|
|
fout.printf(
|
2002-08-20 00:30:13 +00:00
|
|
|
" <persistent_file_xfer>\n"
|
|
|
|
" <num_retries>%d</num_retries>\n"
|
2004-10-25 20:16:30 +00:00
|
|
|
" <first_request_time>%f</first_request_time>\n"
|
|
|
|
" <next_request_time>%f</next_request_time>\n"
|
2003-02-03 22:47:08 +00:00
|
|
|
" <time_so_far>%f</time_so_far>\n"
|
2006-02-18 01:57:24 +00:00
|
|
|
" <last_bytes_xferred>%f</last_bytes_xferred>\n"
|
2002-08-20 00:30:13 +00:00
|
|
|
" </persistent_file_xfer>\n",
|
2006-02-18 01:57:24 +00:00
|
|
|
nretry, first_request_time, next_request_time, time_so_far, last_bytes_xferred
|
2002-08-14 20:31:27 +00:00
|
|
|
);
|
2004-01-31 23:21:07 +00:00
|
|
|
if (fxp) {
|
2004-06-12 04:45:36 +00:00
|
|
|
fout.printf(
|
2004-01-31 23:21:07 +00:00
|
|
|
" <file_xfer>\n"
|
|
|
|
" <bytes_xferred>%f</bytes_xferred>\n"
|
|
|
|
" <file_offset>%f</file_offset>\n"
|
|
|
|
" <xfer_speed>%f</xfer_speed>\n"
|
2006-02-25 00:02:55 +00:00
|
|
|
" <url>%s</url>\n"
|
2004-01-31 23:21:07 +00:00
|
|
|
" </file_xfer>\n",
|
|
|
|
fxp->bytes_xferred,
|
|
|
|
fxp->file_offset,
|
|
|
|
fxp->xfer_speed,
|
2006-02-25 00:02:55 +00:00
|
|
|
fxp->m_url
|
2004-01-31 23:21:07 +00:00
|
|
|
);
|
|
|
|
}
|
2002-08-07 22:52:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-06-08 23:55:21 +00:00
|
|
|
// suspend file transfers by killing them.
|
|
|
|
// They'll restart automatically later.
|
|
|
|
//
|
2003-07-29 23:26:32 +00:00
|
|
|
void PERS_FILE_XFER::suspend() {
|
|
|
|
if (fxp) {
|
2006-02-18 01:57:24 +00:00
|
|
|
last_bytes_xferred = fxp->bytes_xferred; // save bytes transferred
|
2006-02-23 20:37:36 +00:00
|
|
|
if (fxp->is_upload) {
|
|
|
|
last_bytes_xferred += fxp->file_offset;
|
|
|
|
}
|
2003-07-29 23:26:32 +00:00
|
|
|
gstate.file_xfers->remove(fxp); // this removes from http_op_set too
|
|
|
|
delete fxp;
|
|
|
|
fxp = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-08-07 22:52:10 +00:00
|
|
|
PERS_FILE_XFER_SET::PERS_FILE_XFER_SET(FILE_XFER_SET* p) {
|
|
|
|
file_xfers = p;
|
2002-06-10 06:14:18 +00:00
|
|
|
}
|
|
|
|
|
2002-08-21 19:12:42 +00:00
|
|
|
// Run through the set, starting any transfers that need to be
|
|
|
|
// started and deleting any that have finished
|
|
|
|
//
|
2005-06-07 19:22:50 +00:00
|
|
|
bool PERS_FILE_XFER_SET::poll() {
|
2002-08-07 22:52:10 +00:00
|
|
|
unsigned int i;
|
2002-06-10 06:14:18 +00:00
|
|
|
bool action = false;
|
2004-10-14 22:01:05 +00:00
|
|
|
static double last_time=0;
|
|
|
|
|
2005-06-07 19:22:50 +00:00
|
|
|
if (gstate.now - last_time < 1.0) return false;
|
|
|
|
last_time = gstate.now;
|
2002-06-10 06:14:18 +00:00
|
|
|
|
|
|
|
for (i=0; i<pers_file_xfers.size(); i++) {
|
2005-06-07 19:22:50 +00:00
|
|
|
action |= pers_file_xfers[i]->poll();
|
2002-08-07 22:52:10 +00:00
|
|
|
}
|
|
|
|
|
2002-08-22 21:29:58 +00:00
|
|
|
if (action) gstate.set_client_state_dirty("pers_file_xfer_set poll");
|
2002-08-21 19:12:42 +00:00
|
|
|
|
2002-08-07 22:52:10 +00:00
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
2002-08-14 20:31:27 +00:00
|
|
|
// Insert a PERS_FILE_XFER object into the set.
|
|
|
|
// We will decide which ones to start when we hit the polling loop
|
2002-08-07 22:52:10 +00:00
|
|
|
//
|
|
|
|
int PERS_FILE_XFER_SET::insert(PERS_FILE_XFER* pfx) {
|
|
|
|
pers_file_xfers.push_back(pfx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-08-14 20:31:27 +00:00
|
|
|
// Remove a PERS_FILE_XFER object from the set.
|
|
|
|
// What should the action here be?
|
2002-08-07 22:52:10 +00:00
|
|
|
//
|
|
|
|
int PERS_FILE_XFER_SET::remove(PERS_FILE_XFER* pfx) {
|
|
|
|
vector<PERS_FILE_XFER*>::iterator iter;
|
|
|
|
|
|
|
|
iter = pers_file_xfers.begin();
|
|
|
|
while (iter != pers_file_xfers.end()) {
|
|
|
|
if (*iter == pfx) {
|
2006-10-02 17:44:27 +00:00
|
|
|
iter = pers_file_xfers.erase(iter);
|
2002-08-07 22:52:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
iter++;
|
2002-06-10 06:14:18 +00:00
|
|
|
}
|
2003-06-03 22:47:15 +00:00
|
|
|
msg_printf(
|
2007-01-25 23:39:06 +00:00
|
|
|
pfx->fip->project, MSG_INTERNAL_ERROR,
|
2006-01-17 22:48:09 +00:00
|
|
|
"Persistent file transfer object not found"
|
2003-05-14 21:17:58 +00:00
|
|
|
);
|
2004-01-30 22:19:19 +00:00
|
|
|
return ERR_NOT_FOUND;
|
2002-06-10 06:14:18 +00:00
|
|
|
}
|
2003-07-29 23:26:32 +00:00
|
|
|
|
|
|
|
// suspend all PERS_FILE_XFERs
|
|
|
|
//
|
|
|
|
void PERS_FILE_XFER_SET::suspend() {
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i=0; i<pers_file_xfers.size(); i++) {
|
|
|
|
pers_file_xfers[i]->suspend();
|
|
|
|
}
|
|
|
|
}
|
2004-12-08 00:40:19 +00:00
|
|
|
|
2005-01-02 18:29:53 +00:00
|
|
|
const char *BOINC_RCSID_76edfcfb49 = "$Id$";
|